@vtex/faststore 0.0.7 → 0.0.10

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 (498) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/dist/actions.json +194 -1
  3. package/dist/catalog.json +3449 -1612
  4. package/dist/cli/index.d.ts +512 -63
  5. package/dist/cli/index.js +2098 -686
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/server-runtime.js +44 -24
  8. package/dist/cli/server-runtime.js.map +1 -1
  9. package/dist/client.d.ts +5 -1
  10. package/dist/client.js +5 -1
  11. package/dist/components.d.ts +42 -10
  12. package/dist/components.js +47 -15
  13. package/dist/content-types.json +823 -0
  14. package/dist/core/dist/define-component.d.ts +12 -2
  15. package/dist/docs/account-access-gate.md +4 -4
  16. package/dist/docs/account-address-book.md +519 -0
  17. package/dist/docs/account-entry-point.md +5 -5
  18. package/dist/docs/account-menu.md +7 -7
  19. package/dist/docs/account-saved-cards.md +301 -0
  20. package/dist/docs/alert-dialog.md +2 -2
  21. package/dist/docs/billing-address.md +17 -9
  22. package/dist/docs/binding-switcher.md +154 -0
  23. package/dist/docs/card-entry.md +38 -7
  24. package/dist/docs/card-summary.md +175 -0
  25. package/dist/docs/cardholder-document.md +16 -14
  26. package/dist/docs/cart-destination.md +238 -0
  27. package/dist/docs/cart-item.md +22 -4
  28. package/dist/docs/cart-totalizer.md +1 -1
  29. package/dist/docs/cart.md +126 -19
  30. package/dist/docs/client-profile-fieldset.md +7 -5
  31. package/dist/docs/copy-text.md +84 -0
  32. package/dist/docs/document-field.md +8 -8
  33. package/dist/docs/footer-link-group.md +2 -2
  34. package/dist/docs/footer-social.md +1 -1
  35. package/dist/docs/free-shipping-progress.md +13 -9
  36. package/dist/docs/header-root.md +1 -1
  37. package/dist/docs/incentives.md +4 -4
  38. package/dist/docs/logo.md +6 -6
  39. package/dist/docs/my-account-profile.md +4 -4
  40. package/dist/docs/navigation-link-list.md +2 -2
  41. package/dist/docs/navigation-link.md +3 -3
  42. package/dist/docs/order-cancel.md +52 -0
  43. package/dist/docs/order-delivery-address.md +63 -0
  44. package/dist/docs/order-delivery-group.md +83 -0
  45. package/dist/docs/order-details-back.md +41 -0
  46. package/dist/docs/order-details-header.md +48 -0
  47. package/dist/docs/order-list-row.md +46 -0
  48. package/dist/docs/order-list-search.md +101 -0
  49. package/dist/docs/order-list-views.md +97 -0
  50. package/dist/docs/order-list.md +94 -0
  51. package/dist/docs/order-payment.md +94 -0
  52. package/dist/docs/order-placed-date.md +49 -0
  53. package/dist/docs/order-placed-payment-summary.md +13 -15
  54. package/dist/docs/order-placed.md +225 -0
  55. package/dist/docs/order-progress.md +88 -0
  56. package/dist/docs/order-status-badge.md +91 -0
  57. package/dist/docs/order-summary.md +53 -0
  58. package/dist/docs/payment-method-badges.md +16 -5
  59. package/dist/docs/payment-methods.md +1 -1
  60. package/dist/docs/place-order.md +13 -9
  61. package/dist/docs/product-information.md +2 -2
  62. package/dist/docs/product-shelf.md +7 -0
  63. package/dist/docs/saved-cards.md +2 -2
  64. package/dist/docs/select.md +22 -10
  65. package/dist/docs/session.md +1 -1
  66. package/dist/docs/shipping-address.md +1 -1
  67. package/dist/docs/sign-in-redirect.md +7 -7
  68. package/dist/docs/toast.md +35 -13
  69. package/dist/index.d.ts +7 -2
  70. package/dist/index.js +2 -1
  71. package/dist/internal/client.d.ts +9 -1
  72. package/dist/internal/client.js +10 -2
  73. package/dist/internal/server.d.ts +8 -2
  74. package/dist/internal/server.js +8 -2
  75. package/dist/matter/account-access-gate/account-access-gate.d.ts +2 -1
  76. package/dist/matter/account-access-gate/account-access-gate.js +19 -16
  77. package/dist/matter/account-access-gate/account-access-gate.js.map +1 -1
  78. package/dist/matter/account-address-book/account-address-book-delete.d.ts +60 -0
  79. package/dist/matter/account-address-book/account-address-book-delete.js +184 -0
  80. package/dist/matter/account-address-book/account-address-book-delete.js.map +1 -0
  81. package/dist/matter/account-address-book/account-address-book-deliverability.d.ts +19 -0
  82. package/dist/matter/account-address-book/account-address-book-deliverability.js +50 -0
  83. package/dist/matter/account-address-book/account-address-book-deliverability.js.map +1 -0
  84. package/dist/matter/account-address-book/account-address-book-form-parts.d.ts +42 -0
  85. package/dist/matter/account-address-book/account-address-book-form-parts.js +146 -0
  86. package/dist/matter/account-address-book/account-address-book-form-parts.js.map +1 -0
  87. package/dist/matter/account-address-book/account-address-book-profile-notice.d.ts +25 -0
  88. package/dist/matter/account-address-book/account-address-book-profile-notice.js +72 -0
  89. package/dist/matter/account-address-book/account-address-book-profile-notice.js.map +1 -0
  90. package/dist/matter/account-address-book/account-address-book.d.ts +120 -0
  91. package/dist/matter/account-address-book/account-address-book.js +469 -0
  92. package/dist/matter/account-address-book/account-address-book.js.map +1 -0
  93. package/dist/matter/account-address-book/account-address-country-switch.js +59 -0
  94. package/dist/matter/account-address-book/account-address-country-switch.js.map +1 -0
  95. package/dist/matter/account-address-book/account-address-country.d.ts +36 -0
  96. package/dist/matter/account-address-book/account-address-country.js +151 -0
  97. package/dist/matter/account-address-book/account-address-country.js.map +1 -0
  98. package/dist/matter/account-address-book/account-address-first-line.js +11 -0
  99. package/dist/matter/account-address-book/account-address-first-line.js.map +1 -0
  100. package/dist/matter/account-address-book/account-address-form-context.d.ts +55 -0
  101. package/dist/matter/account-address-book/account-address-form-context.js +14 -0
  102. package/dist/matter/account-address-book/account-address-form-context.js.map +1 -0
  103. package/dist/matter/account-address-book/account-address-form.d.ts +85 -0
  104. package/dist/matter/account-address-book/account-address-form.js +946 -0
  105. package/dist/matter/account-address-book/account-address-form.js.map +1 -0
  106. package/dist/matter/account-address-book/account-address-postal-autofill.js +189 -0
  107. package/dist/matter/account-address-book/account-address-postal-autofill.js.map +1 -0
  108. package/dist/matter/account-address-book/account-address-record.d.ts +26 -0
  109. package/dist/matter/account-address-book/account-address-record.js +138 -0
  110. package/dist/matter/account-address-book/account-address-record.js.map +1 -0
  111. package/dist/matter/account-entry-point/account-entry-point.js +11 -11
  112. package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
  113. package/dist/matter/account-menu/account-menu.d.ts +7 -3
  114. package/dist/matter/account-menu/account-menu.js +69 -28
  115. package/dist/matter/account-menu/account-menu.js.map +1 -1
  116. package/dist/matter/account-saved-cards/account-saved-cards.d.ts +383 -0
  117. package/dist/matter/account-saved-cards/account-saved-cards.js +956 -0
  118. package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
  119. package/dist/matter/billing-address/billing-address.d.ts +30 -17
  120. package/dist/matter/billing-address/billing-address.js +128 -33
  121. package/dist/matter/billing-address/billing-address.js.map +1 -1
  122. package/dist/matter/breadcrumb/breadcrumb.js +2 -2
  123. package/dist/matter/breadcrumb/breadcrumb.js.map +1 -1
  124. package/dist/matter/card-entry/card-capture.js +4 -3
  125. package/dist/matter/card-entry/card-capture.js.map +1 -1
  126. package/dist/matter/card-entry/card-entry.d.ts +5 -3
  127. package/dist/matter/card-entry/card-entry.js +77 -27
  128. package/dist/matter/card-entry/card-entry.js.map +1 -1
  129. package/dist/matter/cardholder-document/cardholder-document.d.ts +10 -9
  130. package/dist/matter/cardholder-document/cardholder-document.js +36 -16
  131. package/dist/matter/cardholder-document/cardholder-document.js.map +1 -1
  132. package/dist/matter/cart/cart-namespace.d.ts +5 -1
  133. package/dist/matter/cart/cart-namespace.js +5 -1
  134. package/dist/matter/cart/cart-namespace.js.map +1 -1
  135. package/dist/matter/cart/cart.d.ts +41 -6
  136. package/dist/matter/cart/cart.js +267 -54
  137. package/dist/matter/cart/cart.js.map +1 -1
  138. package/dist/matter/cart-destination/cart-destination.d.ts +222 -0
  139. package/dist/matter/cart-destination/cart-destination.js +613 -0
  140. package/dist/matter/cart-destination/cart-destination.js.map +1 -0
  141. package/dist/matter/cart-item/cart-item.d.ts +4 -2
  142. package/dist/matter/cart-item/cart-item.js +76 -17
  143. package/dist/matter/cart-item/cart-item.js.map +1 -1
  144. package/dist/matter/cart-totalizer/cart-totalizer.js +3 -2
  145. package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
  146. package/dist/matter/client-preferences/client-preferences-fieldset.js +1 -1
  147. package/dist/matter/client-profile-form/client-profile-fieldset.js +8 -5
  148. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -1
  149. package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +6 -3
  150. package/dist/matter/free-shipping-progress/free-shipping-progress.js +76 -33
  151. package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
  152. package/dist/matter/identity-edit/identity-edit.js +3 -3
  153. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  154. package/dist/matter/identity-summary/identity-summary.js +2 -2
  155. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  156. package/dist/matter/my-account-profile/my-account-profile.d.ts +3 -3
  157. package/dist/matter/my-account-profile/my-account-profile.js +4 -4
  158. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  159. package/dist/matter/order-cancel/order-cancel.d.ts +110 -0
  160. package/dist/matter/order-cancel/order-cancel.js +344 -0
  161. package/dist/matter/order-cancel/order-cancel.js.map +1 -0
  162. package/dist/matter/order-delivery-address/order-delivery-address.d.ts +73 -0
  163. package/dist/matter/order-delivery-address/order-delivery-address.js +172 -0
  164. package/dist/matter/order-delivery-address/order-delivery-address.js.map +1 -0
  165. package/dist/matter/order-delivery-group/order-delivery-group.d.ts +128 -0
  166. package/dist/matter/order-delivery-group/order-delivery-group.js +408 -0
  167. package/dist/matter/order-delivery-group/order-delivery-group.js.map +1 -0
  168. package/dist/matter/order-details-header/order-details-header.d.ts +104 -0
  169. package/dist/matter/order-details-header/order-details-header.js +190 -0
  170. package/dist/matter/order-details-header/order-details-header.js.map +1 -0
  171. package/dist/matter/order-list/customer-orders-filter.d.ts +12 -0
  172. package/dist/matter/order-list/customer-orders-filter.js +30 -0
  173. package/dist/matter/order-list/customer-orders-filter.js.map +1 -0
  174. package/dist/matter/order-list/order-list.d.ts +121 -0
  175. package/dist/matter/order-list/order-list.js +395 -0
  176. package/dist/matter/order-list/order-list.js.map +1 -0
  177. package/dist/matter/order-list-row/order-list-row.d.ts +44 -0
  178. package/dist/matter/order-list-row/order-list-row.js +127 -0
  179. package/dist/matter/order-list-row/order-list-row.js.map +1 -0
  180. package/dist/matter/order-list-search/order-list-search.d.ts +72 -0
  181. package/dist/matter/order-list-search/order-list-search.js +296 -0
  182. package/dist/matter/order-list-search/order-list-search.js.map +1 -0
  183. package/dist/matter/order-list-views/order-list-views.d.ts +65 -0
  184. package/dist/matter/order-list-views/order-list-views.js +154 -0
  185. package/dist/matter/order-list-views/order-list-views.js.map +1 -0
  186. package/dist/matter/order-payment/order-payment.d.ts +83 -0
  187. package/dist/matter/order-payment/order-payment.js +258 -0
  188. package/dist/matter/order-payment/order-payment.js.map +1 -0
  189. package/dist/matter/order-placed/order-placed-deliveries.d.ts +145 -0
  190. package/dist/matter/order-placed/order-placed-deliveries.js +380 -0
  191. package/dist/matter/order-placed/order-placed-deliveries.js.map +1 -0
  192. package/dist/matter/order-placed/order-placed.d.ts +122 -0
  193. package/dist/matter/order-placed/order-placed.js +452 -0
  194. package/dist/matter/order-placed/order-placed.js.map +1 -0
  195. package/dist/matter/order-placed-date/order-placed-date.d.ts +45 -0
  196. package/dist/matter/order-placed-date/order-placed-date.js +92 -0
  197. package/dist/matter/order-placed-date/order-placed-date.js.map +1 -0
  198. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +5 -8
  199. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +29 -81
  200. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -1
  201. package/dist/matter/order-progress/order-progress.d.ts +85 -0
  202. package/dist/matter/order-progress/order-progress.js +203 -0
  203. package/dist/matter/order-progress/order-progress.js.map +1 -0
  204. package/dist/matter/order-summary/order-summary.d.ts +81 -0
  205. package/dist/matter/order-summary/order-summary.js +192 -0
  206. package/dist/matter/order-summary/order-summary.js.map +1 -0
  207. package/dist/matter/payment/cardholder-document-rule.d.ts +35 -0
  208. package/dist/matter/payment/cardholder-document-rule.js +35 -0
  209. package/dist/matter/payment/cardholder-document-rule.js.map +1 -0
  210. package/dist/matter/payment/payment-card-capture.js +38 -23
  211. package/dist/matter/payment/payment-card-capture.js.map +1 -1
  212. package/dist/matter/payment/payment-controller.js +11 -0
  213. package/dist/matter/payment/payment-controller.js.map +1 -1
  214. package/dist/matter/payment/payment-method-badges.js +4 -2
  215. package/dist/matter/payment/payment-method-badges.js.map +1 -1
  216. package/dist/matter/payment/payment.js +11 -0
  217. package/dist/matter/payment/payment.js.map +1 -1
  218. package/dist/matter/payment/use-payment-method-selection.d.ts +39 -0
  219. package/dist/matter/payment/use-payment-method-selection.js +30 -2
  220. package/dist/matter/payment/use-payment-method-selection.js.map +1 -1
  221. package/dist/matter/payment/use-payment-submit-gate.d.ts +16 -2
  222. package/dist/matter/payment/use-payment-submit-gate.js +42 -14
  223. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -1
  224. package/dist/matter/place-order/place-order.d.ts +12 -7
  225. package/dist/matter/place-order/place-order.js +126 -103
  226. package/dist/matter/place-order/place-order.js.map +1 -1
  227. package/dist/matter/pricing/pricing.js +5 -5
  228. package/dist/matter/pricing/pricing.js.map +1 -1
  229. package/dist/matter/product-shelf/product-shelf.d.ts +7 -0
  230. package/dist/matter/product-shelf/product-shelf.js +115 -1
  231. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  232. package/dist/matter/region/region.js +1 -1
  233. package/dist/matter/saved-cards/saved-cards.js +26 -15
  234. package/dist/matter/saved-cards/saved-cards.js.map +1 -1
  235. package/dist/matter/session/session.js +2 -2
  236. package/dist/matter/session/session.js.map +1 -1
  237. package/dist/matter/shipping-address/country-descriptors.d.ts +1 -0
  238. package/dist/matter/shipping-address/country-descriptors.js +23 -1
  239. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  240. package/dist/matter/shipping-address/shipping-address.js +62 -14
  241. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  242. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +2 -2
  243. package/dist/matter/shipping-simulator/shipping-simulator.js +6 -2
  244. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  245. package/dist/matter/sign-in-redirect/sign-in-redirect.js +48 -10
  246. package/dist/matter/sign-in-redirect/sign-in-redirect.js.map +1 -1
  247. package/dist/matter/variant-selector/variant-selector.js +2 -2
  248. package/dist/matter/variant-selector/variant-selector.js.map +1 -1
  249. package/dist/particles/address-summary/address-summary.js +2 -2
  250. package/dist/particles/address-summary/address-summary.js.map +1 -1
  251. package/dist/particles/alert-dialog/alert-dialog.d.ts +5 -1
  252. package/dist/particles/alert-dialog/alert-dialog.js +5 -2
  253. package/dist/particles/alert-dialog/alert-dialog.js.map +1 -1
  254. package/dist/particles/binding-switcher/binding-switcher.d.ts +38 -0
  255. package/dist/particles/binding-switcher/binding-switcher.js +183 -0
  256. package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
  257. package/dist/particles/card-summary/card-network.d.ts +17 -0
  258. package/dist/particles/card-summary/card-network.js +25 -0
  259. package/dist/particles/card-summary/card-network.js.map +1 -0
  260. package/dist/particles/card-summary/card-summary.d.ts +83 -0
  261. package/dist/particles/card-summary/card-summary.js +188 -0
  262. package/dist/particles/card-summary/card-summary.js.map +1 -0
  263. package/dist/particles/copy-text/copy-text.d.ts +28 -0
  264. package/dist/particles/copy-text/copy-text.js +140 -0
  265. package/dist/particles/copy-text/copy-text.js.map +1 -0
  266. package/dist/particles/document-field/document-field.d.ts +34 -5
  267. package/dist/particles/document-field/document-field.js +115 -19
  268. package/dist/particles/document-field/document-field.js.map +1 -1
  269. package/dist/particles/field/field.js +1 -1
  270. package/dist/particles/field/field.js.map +1 -1
  271. package/dist/particles/footer-link-group/footer-link-group.d.ts +6 -2
  272. package/dist/particles/footer-link-group/footer-link-group.js +18 -7
  273. package/dist/particles/footer-link-group/footer-link-group.js.map +1 -1
  274. package/dist/particles/footer-social/footer-social.d.ts +1 -1
  275. package/dist/particles/footer-social/footer-social.js +1 -1
  276. package/dist/particles/footer-social/footer-social.js.map +1 -1
  277. package/dist/particles/incentives/incentives.d.ts +7 -3
  278. package/dist/particles/incentives/incentives.js +8 -3
  279. package/dist/particles/incentives/incentives.js.map +1 -1
  280. package/dist/particles/logo/logo.d.ts +3 -2
  281. package/dist/particles/logo/logo.js +20 -8
  282. package/dist/particles/logo/logo.js.map +1 -1
  283. package/dist/particles/navigation-link/navigation-link.d.ts +2 -3
  284. package/dist/particles/navigation-link/navigation-link.js +4 -4
  285. package/dist/particles/navigation-link/navigation-link.js.map +1 -1
  286. package/dist/particles/navigation-link-list/navigation-link-list.d.ts +6 -2
  287. package/dist/particles/navigation-link-list/navigation-link-list.js +18 -7
  288. package/dist/particles/navigation-link-list/navigation-link-list.js.map +1 -1
  289. package/dist/particles/order-details-back/order-details-back.d.ts +28 -0
  290. package/dist/particles/order-details-back/order-details-back.js +52 -0
  291. package/dist/particles/order-details-back/order-details-back.js.map +1 -0
  292. package/dist/particles/order-status-badge/order-status-badge.d.ts +19 -0
  293. package/dist/particles/order-status-badge/order-status-badge.js +50 -0
  294. package/dist/particles/order-status-badge/order-status-badge.js.map +1 -0
  295. package/dist/particles/payment-methods/payment-methods.d.ts +1 -1
  296. package/dist/particles/payment-methods/payment-methods.js +1 -1
  297. package/dist/particles/payment-methods/payment-methods.js.map +1 -1
  298. package/dist/particles/phone-field/phone-field.js +2 -2
  299. package/dist/particles/phone-field/phone-field.js.map +1 -1
  300. package/dist/particles/select/select.js +6 -6
  301. package/dist/particles/select/select.js.map +1 -1
  302. package/dist/particles/toast/toast.d.ts +21 -7
  303. package/dist/particles/toast/toast.js +76 -8
  304. package/dist/particles/toast/toast.js.map +1 -1
  305. package/dist/schema.graphql +1376 -126
  306. package/dist/sdk/actions/address-action-provider.d.ts +16 -0
  307. package/dist/sdk/actions/address-action-provider.js +20 -0
  308. package/dist/sdk/actions/address-action-provider.js.map +1 -0
  309. package/dist/sdk/actions/analytics-action-provider.js +1 -1
  310. package/dist/sdk/actions/index.d.ts +5 -1
  311. package/dist/sdk/actions/index.js +5 -1
  312. package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
  313. package/dist/sdk/actions/locale-action-provider.js +20 -0
  314. package/dist/sdk/actions/locale-action-provider.js.map +1 -0
  315. package/dist/sdk/actions/order-action-provider.d.ts +12 -0
  316. package/dist/sdk/actions/order-action-provider.js +16 -0
  317. package/dist/sdk/actions/order-action-provider.js.map +1 -0
  318. package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
  319. package/dist/sdk/actions/saved-card-action-provider.js +16 -0
  320. package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
  321. package/dist/sdk/actions/types.d.ts +10 -2
  322. package/dist/sdk/address/address-fields.js +38 -0
  323. package/dist/sdk/address/address-fields.js.map +1 -0
  324. package/dist/sdk/address/checkout-readiness.js +17 -0
  325. package/dist/sdk/address/checkout-readiness.js.map +1 -0
  326. package/dist/sdk/address/constants.js +60 -0
  327. package/dist/sdk/address/constants.js.map +1 -0
  328. package/dist/sdk/address/country-display.js +19 -0
  329. package/dist/sdk/address/country-display.js.map +1 -0
  330. package/dist/sdk/address/country-rules.js +30 -0
  331. package/dist/sdk/address/country-rules.js.map +1 -0
  332. package/dist/sdk/address/format-address.js +18 -0
  333. package/dist/sdk/address/format-address.js.map +1 -0
  334. package/dist/sdk/address/iso-countries.js +262 -0
  335. package/dist/sdk/address/iso-countries.js.map +1 -0
  336. package/dist/sdk/address/layouts.js +56 -0
  337. package/dist/sdk/address/layouts.js.map +1 -0
  338. package/dist/sdk/address/messages.js +97 -0
  339. package/dist/sdk/address/messages.js.map +1 -0
  340. package/dist/sdk/address/operations.d.ts +54 -0
  341. package/dist/sdk/address/operations.js +83 -0
  342. package/dist/sdk/address/operations.js.map +1 -0
  343. package/dist/sdk/address/postal-code-lookup-operations.js +23 -0
  344. package/dist/sdk/address/postal-code-lookup-operations.js.map +1 -0
  345. package/dist/sdk/address/postal-code.js +11 -0
  346. package/dist/sdk/address/postal-code.js.map +1 -0
  347. package/dist/sdk/address/schema.js +14 -0
  348. package/dist/sdk/address/schema.js.map +1 -0
  349. package/dist/sdk/address/server-fields.js +25 -0
  350. package/dist/sdk/address/server-fields.js.map +1 -0
  351. package/dist/sdk/address/switch-country.js +57 -0
  352. package/dist/sdk/address/switch-country.js.map +1 -0
  353. package/dist/sdk/address/types.d.ts +26 -0
  354. package/dist/sdk/address/use-address-actions.d.ts +29 -0
  355. package/dist/sdk/address/use-address-actions.js +247 -0
  356. package/dist/sdk/address/use-address-actions.js.map +1 -0
  357. package/dist/sdk/address/use-address-by-postal-code.js +57 -0
  358. package/dist/sdk/address/use-address-by-postal-code.js.map +1 -0
  359. package/dist/sdk/address/validate-address.js +62 -0
  360. package/dist/sdk/address/validate-address.js.map +1 -0
  361. package/dist/sdk/cache/storefront-cache.js +30 -4
  362. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  363. package/dist/sdk/cart/operations.d.ts +2 -1
  364. package/dist/sdk/cart/operations.js +55 -1
  365. package/dist/sdk/cart/operations.js.map +1 -1
  366. package/dist/sdk/cart/optimistic.d.ts +30 -7
  367. package/dist/sdk/cart/optimistic.js +78 -41
  368. package/dist/sdk/cart/optimistic.js.map +1 -1
  369. package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
  370. package/dist/sdk/cart/use-cart-actions.js +61 -15
  371. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  372. package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
  373. package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
  374. package/dist/sdk/client/fetch-page-data.d.ts +11 -0
  375. package/dist/sdk/client/fetch-page-data.js +12 -1
  376. package/dist/sdk/client/fetch-page-data.js.map +1 -1
  377. package/dist/sdk/content/create-content-runtime.js +1 -8
  378. package/dist/sdk/content/create-content-runtime.js.map +1 -1
  379. package/dist/sdk/data/failed-fields.js +23 -0
  380. package/dist/sdk/data/failed-fields.js.map +1 -0
  381. package/dist/sdk/data/index.js +2 -2
  382. package/dist/sdk/data/page-reactive-data-root.d.ts +45 -7
  383. package/dist/sdk/data/page-reactive-data-root.js +88 -12
  384. package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
  385. package/dist/sdk/data/reactive-list-item-data-provider.js +2 -2
  386. package/dist/sdk/generated/fragment-types.js +2 -0
  387. package/dist/sdk/generated/fragment-types.js.map +1 -1
  388. package/dist/sdk/generated/gql.js +17 -11
  389. package/dist/sdk/generated/gql.js.map +1 -1
  390. package/dist/sdk/generated/graphql.d.ts +185 -46
  391. package/dist/sdk/generated/graphql.js +2587 -155
  392. package/dist/sdk/generated/graphql.js.map +1 -1
  393. package/dist/sdk/i18n/messages/en-US.js +157 -1
  394. package/dist/sdk/i18n/messages/en-US.json +157 -1
  395. package/dist/sdk/i18n/messages/en-US.json.d.ts +156 -0
  396. package/dist/sdk/i18n/messages/es-ES.js +157 -1
  397. package/dist/sdk/i18n/messages/es-ES.json +157 -1
  398. package/dist/sdk/i18n/messages/pt-BR.js +157 -1
  399. package/dist/sdk/i18n/messages/pt-BR.json +157 -1
  400. package/dist/sdk/i18n/provider.js +1 -1
  401. package/dist/sdk/i18n/provider.js.map +1 -1
  402. package/dist/sdk/locale/binding-switch-provider.d.ts +13 -0
  403. package/dist/sdk/locale/binding-switch-provider.js +20 -0
  404. package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
  405. package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
  406. package/dist/sdk/locale/use-locale-actions.js +70 -0
  407. package/dist/sdk/locale/use-locale-actions.js.map +1 -0
  408. package/dist/sdk/navigation/binding-switch.d.ts +39 -0
  409. package/dist/sdk/navigation/binding-switch.js +129 -0
  410. package/dist/sdk/navigation/binding-switch.js.map +1 -0
  411. package/dist/sdk/navigation/current-page.js +30 -0
  412. package/dist/sdk/navigation/current-page.js.map +1 -0
  413. package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
  414. package/dist/sdk/navigation/decode-route-params.js +32 -0
  415. package/dist/sdk/navigation/decode-route-params.js.map +1 -0
  416. package/dist/sdk/navigation/not-found-log.d.ts +28 -0
  417. package/dist/sdk/navigation/not-found-log.js +133 -0
  418. package/dist/sdk/navigation/not-found-log.js.map +1 -0
  419. package/dist/sdk/navigation/path.js +25 -0
  420. package/dist/sdk/navigation/path.js.map +1 -0
  421. package/dist/sdk/navigation/redirect.d.ts +11 -0
  422. package/dist/sdk/navigation/redirect.js +22 -0
  423. package/dist/sdk/navigation/redirect.js.map +1 -0
  424. package/dist/sdk/navigation/report.js +70 -0
  425. package/dist/sdk/navigation/report.js.map +1 -0
  426. package/dist/sdk/navigation/resolve-storefront-href.d.ts +12 -0
  427. package/dist/sdk/navigation/resolve-storefront-href.js +214 -0
  428. package/dist/sdk/navigation/resolve-storefront-href.js.map +1 -0
  429. package/dist/sdk/navigation/route-identity-provider.d.ts +11 -0
  430. package/dist/sdk/navigation/route-identity-provider.js +20 -0
  431. package/dist/sdk/navigation/route-identity-provider.js.map +1 -0
  432. package/dist/sdk/navigation/route-map-provider.d.ts +19 -0
  433. package/dist/sdk/navigation/route-map-provider.js +29 -0
  434. package/dist/sdk/navigation/route-map-provider.js.map +1 -0
  435. package/dist/sdk/navigation/storefront-link.d.ts +16 -0
  436. package/dist/sdk/navigation/storefront-link.js +66 -0
  437. package/dist/sdk/navigation/storefront-link.js.map +1 -0
  438. package/dist/sdk/navigation/types.d.ts +45 -0
  439. package/dist/sdk/navigation/use-storefront-navigation.d.ts +26 -0
  440. package/dist/sdk/navigation/use-storefront-navigation.js +73 -0
  441. package/dist/sdk/navigation/use-storefront-navigation.js.map +1 -0
  442. package/dist/sdk/navigation/use-storefront-route.d.ts +26 -0
  443. package/dist/sdk/navigation/use-storefront-route.js +87 -0
  444. package/dist/sdk/navigation/use-storefront-route.js.map +1 -0
  445. package/dist/sdk/navigation/use-storefront-search-params.d.ts +11 -0
  446. package/dist/sdk/navigation/use-storefront-search-params.js +16 -0
  447. package/dist/sdk/navigation/use-storefront-search-params.js.map +1 -0
  448. package/dist/sdk/orders/completion-badge.d.ts +8 -0
  449. package/dist/sdk/orders/completion-badge.js +47 -0
  450. package/dist/sdk/orders/completion-badge.js.map +1 -0
  451. package/dist/sdk/orders/order-operations.d.ts +19 -0
  452. package/dist/sdk/orders/order-operations.js +21 -0
  453. package/dist/sdk/orders/order-operations.js.map +1 -0
  454. package/dist/sdk/orders/use-order-actions.d.ts +23 -0
  455. package/dist/sdk/orders/use-order-actions.js +62 -0
  456. package/dist/sdk/orders/use-order-actions.js.map +1 -0
  457. package/dist/sdk/payment/gateway-origin.js +26 -13
  458. package/dist/sdk/payment/gateway-origin.js.map +1 -1
  459. package/dist/sdk/payment/gateway.d.ts +14 -2
  460. package/dist/sdk/payment/gateway.js +32 -14
  461. package/dist/sdk/payment/gateway.js.map +1 -1
  462. package/dist/sdk/payment/operations.js +11 -0
  463. package/dist/sdk/payment/operations.js.map +1 -1
  464. package/dist/sdk/payment/submission-registry.d.ts +2 -1
  465. package/dist/sdk/payment/submission-registry.js +12 -8
  466. package/dist/sdk/payment/submission-registry.js.map +1 -1
  467. package/dist/sdk/payment/use-payment-actions.d.ts +5 -3
  468. package/dist/sdk/payment/use-payment-actions.js +5 -2
  469. package/dist/sdk/payment/use-payment-actions.js.map +1 -1
  470. package/dist/sdk/payment/use-place-order.d.ts +6 -3
  471. package/dist/sdk/payment/use-place-order.js +333 -143
  472. package/dist/sdk/payment/use-place-order.js.map +1 -1
  473. package/dist/sdk/saved-cards/operations.d.ts +16 -0
  474. package/dist/sdk/saved-cards/operations.js +41 -0
  475. package/dist/sdk/saved-cards/operations.js.map +1 -0
  476. package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
  477. package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
  478. package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
  479. package/dist/sdk/seo/page-alternates.d.ts +57 -0
  480. package/dist/sdk/seo/page-alternates.js +127 -0
  481. package/dist/sdk/seo/page-alternates.js.map +1 -0
  482. package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
  483. package/dist/sdk/shipping/shipping-operations.js +13 -0
  484. package/dist/sdk/shipping/shipping-operations.js.map +1 -1
  485. package/dist/sdk/state/cart.d.ts +9 -2
  486. package/dist/sdk/state/cart.js +9 -2
  487. package/dist/sdk/state/cart.js.map +1 -1
  488. package/dist/utils/own-value.js +19 -0
  489. package/dist/utils/own-value.js.map +1 -0
  490. package/dist/utils/safe-href.d.ts +4 -4
  491. package/dist/utils/safe-href.js +31 -22
  492. package/dist/utils/safe-href.js.map +1 -1
  493. package/package.json +5 -1
  494. package/skills/storefront-asset/SKILL.md +111 -0
  495. package/skills/storefront-asset/references/api-side.md +153 -0
  496. package/skills/storefront-asset/references/findings.md +40 -0
  497. package/skills/storefront-asset/references/ui-side.md +94 -0
  498. package/src/styles/tailwind.css +28 -2
@@ -39,6 +39,15 @@ CartList (iterator — renders its children once per cart line)
39
39
  - **`CartItemRoot` is required.** Every `CartItem*` core particle reads its data from the nearest `CartItemRoot` (build-time `requiresContext: 'cart-item-data'`). Placing a core particle directly inside `CartList` with no `CartItemRoot` between them is a build error. When authoring a cart or mini-cart line template, always insert a `CartItemRoot` between the `CartList` and the row's particles — it carries no visible styling of its own, so put the row layout classes on it (or on a `Container` inside it).
40
40
  - **`CartItemRoot` must be inside `CartList`.** It requires the `cart-item` iteration context that `CartList` provides.
41
41
 
42
+ ## Image placeholder
43
+
44
+ `CartItemImage` renders the `<img>` while the line has a usable image. It renders a placeholder `span` in the same slot when:
45
+
46
+ - the line has no image, or its URL is blank; or
47
+ - the image fails to load, including a failure that happened before hydration (an image already settled with no pixels at mount is treated as failed).
48
+
49
+ The placeholder carries `data-fs-cart-item-image` and `data-fs-cart-item-image-broken`, and not `data-fs-image` because it is not an image element. It takes the image's `className` and `style`, so the slot keeps its size. It is hidden from assistive technology (`aria-hidden`, no role or name), so only a loaded image is announced. It has no built-in look: style it with a swatch keyed on the hook, for example `data-[fs-cart-item-image-broken]:bg-gray-100`. The placeholder keeps the image's display (`inline-block`, set inline) so the sizing classes keep working with no extra utility; a swatch overrides it only with an important utility, and the look stays the swatch's. A failure is remembered only for the URL that failed, so a line whose URL changes tries the new one. A consumer `onError` still runs when the browser delivers the `error` event; a failure found at mount is not reported through it. A `ref` reaches the `<img>` only.
50
+
42
51
  ## PDP link composition
43
52
 
44
53
  `CartItemProductLink` links a line to its product page. It reads the `product.detailUrl` the core slice already carries, so it owns no fragment and placing it adds nothing to the page query. Wrap the image in it:
@@ -46,14 +55,14 @@ CartList (iterator — renders its children once per cart line)
46
55
  ```text
47
56
  CartItemRoot
48
57
  └─ CartItemProductLink (anchor to this line's product page)
49
- └─ CartItemImage (unchanged — its alt text names the link)
58
+ └─ CartItemImage (the link names itself with aria-label; see below)
50
59
  ```
51
60
 
52
- **Placing it is the switch.** There is no prop to turn the link on or off: a mini-cart line composes it, a checkout line omits it. That is why `CartItemImage` on its own still renders a plain `<img>` with no anchor, exactly as before.
61
+ **Placing it is the switch.** There is no prop to turn the link on or off: a mini-cart line composes it, a checkout line omits it. That is why `CartItemImage` on its own renders the `<img>` (or, with no usable image, the placeholder described below) and never an anchor.
53
62
 
54
- **It names itself from what it wraps.** The link adds no `aria-label`, so its accessible name is the image's alt text (`image.altText ?? variant.name`). On a line whose `variant.image` is `null` the leaf renders its children unwrapped instead of wrapping nothing, because an anchor with no content is a keyboard focus stop with no accessible name.
63
+ **It names itself.** The link sets `aria-label` to the image's alt text (`image.altText ?? variant.name`, trimmed), so it keeps that name when the wrapped image is replaced by the hidden placeholder (a load error, or a blank URL). A loaded image gives the same name as before. An `aria-label` passed to the link wins over it. On a line whose `variant.image` is `null`, or whose name is blank, the leaf still renders its children unwrapped instead of wrapping nothing, because an anchor with no accessible name is a keyboard focus stop.
55
64
 
56
- **No usable product URL, no link.** The leaf runs `detailUrl` through `resolveSafeHref`, the same allowlist the navigation particle applies to CMS-authored destinations, and renders its children unwrapped when the destination is blank or refused: `javascript:`, `data:`, protocol-relative, or malformed. The particle gates only `$content.href`, so a platform URL arriving on the `href` prop is this leaf's to validate. Either way the image still shows: the leaf never self-hides, unlike the data-bearing leaves below, because hiding a wrapper would take its content with it. A refusal logs its reason and never the value; a blank URL is ordinary and stays quiet.
65
+ **No usable product URL, no link.** The leaf runs `detailUrl` through `resolveSafeHref`, the same allowlist the navigation particle applies to CMS-authored destinations, and renders its children unwrapped when the destination is blank or refused: `javascript:`, `data:`, protocol-relative, or malformed. A safe `detailUrl` is a logical path: `NavigationLink` prefixes it for the current binding. The particle gates only `$content.href`, so a platform URL arriving on the `href` prop is this leaf's to validate. Either way the image still shows: the leaf never self-hides, unlike the data-bearing leaves below, because hiding a wrapper would take its content with it. A refusal logs its reason and never the value and still renders the children unwrapped; a blank URL is ordinary and stays quiet.
57
66
 
58
67
  Navigation is client-side through the `NavigationLink` particle, which also owns `rel` hardening. The link is same-tab, and `target` is omitted from the props type rather than merely left out of the schema, because an image-only link cannot carry the "(opens in new tab)" cue a `_blank` destination needs.
59
68
 
@@ -76,6 +85,15 @@ CartItemRoot
76
85
 
77
86
  For the cart-level counterparts (the "N items unavailable" summary and the bulk "remove all unavailable" button), see `cart.md` — Unavailable-item recovery.
78
87
 
88
+ ## Removal feedback
89
+
90
+ `CartItemRemove` awaits the removal and tells the shopper when it went through:
91
+
92
+ - **Disabled while pending.** The button carries the native `disabled` attribute from the click until `removeItem` settles, so a second click sends no second request. A caller-passed `disabled` is kept.
93
+ - **Toast on a clean result only.** When `removeItem` resolves with an empty `userErrors` list, the button raises one `success` toast titled `removedMessage` (default `Item removed`, editable per store). A non-empty list, a missing `removeItem`, or a rejection raises no toast; failures keep surfacing through `CartError`.
94
+ - **`removedMessage`** is the only schema prop. The toast carries the title alone: no description, no action.
95
+ - **Needs a `ToastProvider`.** The toast shows through the layout's `ToastList`. Without a `ToastProvider` above the line, no toast shows and the removal still works.
96
+
79
97
  ## Gift lines
80
98
 
81
99
  A promotion can add a line the shopper never chose. `CartItem.gift` marks it, and three things follow from that one flag — none of them requiring a different template, because `CartList` renders the same children for every row:
@@ -49,7 +49,7 @@ CartTotalizerRoot ← reactive data provider (totalizers + total)
49
49
  | Component | Props | Notes |
50
50
  |---|---|---|
51
51
  | `CartTotalizerRoot` | `$data` | Provides `CartTotalizerContext` and `CartTotalValueContext`; lines format from the active storefront context |
52
- | `CartTotalizerLine` | `totalizerId`, `label?`, `variant?`, `zeroLabel?` | `variant="discount"` auto-hides when `amount === 0` |
52
+ | `CartTotalizerLine` | `totalizerId`, `label?`, `variant?`, `zeroLabel?` | `variant="discount"` auto-hides when the amount is numerically zero, whatever its wire form (`0`, `"0.00"`, `"-0.00"`); in any variant the line renders nothing when the amount is not a finite number |
53
53
  | `CartDiscountLine` | `otherLabel?` | Itemized discount breakdown: one line per promotion/coupon plus the residual. Self-hides when empty. Owns its own `Cart.discounts` slice |
54
54
  | `CartMarkdownLine` | `label?` | Aggregate item-level catalog markdown (`Σ listPrice − price`): a single negative line, self-hides at zero. Distinct money from the `Discounts` totalizer / `CartDiscountLine` — may be shown alongside them. Owns its own `Cart.itemMarkdownTotal` slice |
55
55
  | `CartTotal` | `label?` | The single root-agnostic grand total; reads `CartTotalValueContext` provided by either root. Set `label` to render a labeled total row (e.g. "Total") |
package/dist/docs/cart.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Cart **writes** (add / remove / quantity) no longer flow through `CartRoot`. A component declares the cart methods it needs as an `actions: { cart: [...] }` selector on its definition and dispatches through the compiler-injected `$actions.cart.*` prop — the write-side mirror of `fragment` → `$data`. Cart writes are **context-free**: an add-to-cart button works on a PDP, a product card, a search result, or the header with **no `CartRoot` ancestor** (docs/storefront-core/reference-free-particle-model.md § Write side — actions, ADR-0010).
7
7
 
8
- This doc covers `CartRoot` — hosted in the layout, never emitted in page schemas — plus the **mini-cart footer checkout CTA** ([below](#mini-cart-footer-checkout-cta)), the composition of `CartCheckoutGuard` → (`CartTotal` + `CartGoCheckout`). The remaining cart line-item and summary particles (`CartList`, `CartItem*`, `CartCount`, `CartError`) exist in the catalog and will be documented when the full cart page enters scope.
8
+ This doc covers `CartRoot` — hosted in the layout, never emitted in page schemas — plus the **mini-cart footer checkout CTA** ([below](#mini-cart-footer-checkout-cta)), the composition of `CartCheckoutGuard` → (`CartTotal` + `CartGoCheckout`). The remaining cart line-item and summary particles (`CartList`, `CartItem*`, `CartError`) exist in the catalog and will be documented when the full cart page enters scope. `CartCount` is documented in [Item count](#item-count).
9
9
 
10
10
  ---
11
11
 
@@ -61,11 +61,11 @@ Cart **write** consumers (`BuyButtonButton`, `ProductCardAddToCart`, `CartItemQu
61
61
 
62
62
  ## Post-add behavior (open the mini-cart vs. redirect to checkout)
63
63
 
64
- What happens after a successful add to cart is a **per-store choice made by composition**, not a config flag. The store composes exactly one of two behavior components — there is no `global.json` switch deciding it.
64
+ What happens after a successful add to cart is a **per-store choice made by composition**, not a config flag. The store composes exactly one of two behavior components — there is no foundation switch deciding it.
65
65
 
66
66
  - **Mini-cart store → `CartOpenOnAdd`.** When the store has a mini-cart drawer, compose `CartOpenOnAdd` as the **parent of the mini-cart `DrawerRoot`** (its single child). It opens the drawer automatically on each successful add, with the new item visible and the count updated. It controls the drawer internally, so **do not set raw `open` / `onOpenChange` on `DrawerRoot`** — those are not authorable props, and the header `DrawerTrigger` plus manual close (Escape / backdrop / close button) keep working through the encapsulated wiring.
67
67
 
68
- - **No-mini-cart store → `CartRedirectOnAdd`.** When the store has **no** mini-cart and adding a product should send the shopper straight to checkout, compose `CartRedirectOnAdd` (and omit the drawer entirely). It navigates to `href` (default `/checkout`) after each successful add. It renders no UI of its own; place it once in the layout.
68
+ - **No-mini-cart store → `CartRedirectOnAdd`.** When the store has **no** mini-cart and adding a product should send the shopper straight to checkout, compose `CartRedirectOnAdd` (and omit the drawer entirely). It navigates to `href` (default the checkout page, `{ "standard": "checkout" }`) after each successful add. It renders no UI of its own; place it once in the layout.
69
69
 
70
70
  The two are **mutually exclusive — compose exactly one**. A store with a mini-cart uses `CartOpenOnAdd` and never `CartRedirectOnAdd`; a store without one uses `CartRedirectOnAdd` and omits the drawer. Both react only to a **successful** add (a failed add or one returning user errors triggers neither).
71
71
 
@@ -87,7 +87,7 @@ CartRoot (layout)
87
87
  ```
88
88
  CartRoot (layout)
89
89
  ├── Header (add-to-cart CTAs elsewhere; no drawer)
90
- └── CartRedirectOnAdd ← navigates to /checkout on a successful add
90
+ └── CartRedirectOnAdd ← navigates to the checkout page on a successful add
91
91
  ```
92
92
 
93
93
  ---
@@ -145,22 +145,28 @@ It is the drawer's own close control, so it **requires a `DrawerRoot` ancestor**
145
145
 
146
146
  ## Coupon
147
147
 
148
- Four root-agnostic components wire up the cart coupon flow — apply a code, list the applied coupons, remove one, and surface apply/remove errors. They read `useCart()` (for the applied list) and dispatch through `$actions.cart.applyCoupon` / `$actions.cart.removeCoupon`, so they compose anywhere inside a `CartRoot` subtree. In a mini-cart they sit in the `DrawerPopup` footer, above the totals.
148
+ Six root-agnostic components wire up the cart coupon flow — apply a code, list the applied coupons, remove one, surface apply/remove errors, gate the entry on a single-coupon store, and tell the shopper when an applied coupon is not being applied. They read `useCart()` (for the applied list) and dispatch through `$actions.cart.applyCoupon` / `$actions.cart.removeCoupon`, so they compose anywhere inside a `CartRoot` subtree. In a mini-cart they sit in the `DrawerPopup` footer, above the totals.
149
149
 
150
150
  The applied coupons are a **list of chips (1..N)**, not a single-coupon slot — forward-compatible with multi-coupon stores. The server recomputes totals on every apply/remove and returns the updated cart, so `CartTotal` and the totalizer rows update automatically; no optimistic state is used.
151
151
 
152
152
  | Component | Element | Purpose | Notes |
153
153
  |-----------|---------|---------|-------|
154
- | `CartCouponForm` | `form` | Coupon code input + apply button | Dispatches `$actions.cart.applyCoupon`. Disables input + button and exposes `data-fs-cart-coupon-pending` while an apply is in flight; clears the input on success. |
155
- | `CartAppliedCoupons` | `div` | Renders one chip per applied coupon | Reads `cart.appliedCoupons`; renders nothing when empty (no container). Each chip shows the `code` inline and a `CartCouponRemove`. An applied-but-no-discount coupon (`isUnmatchedCondition`) is exposed via the `data-fs-cart-coupon-unmatched` hook for the store to style/label (no baked copy). |
154
+ | `CartCouponForm` | `form` | Coupon code input + apply button | Dispatches `$actions.cart.applyCoupon`. Disables input + button and exposes `data-fs-cart-coupon-pending` while an apply is in flight; clears the input on success. Raises one success toast titled `appliedMessage` (default `Coupon applied`) only when the apply returns no errors and the submitted code comes back matched (compared ignoring case); an unmatched code, an error or a blank or `null` `appliedMessage` raises none. The toast still shows when `CartCouponEmpty` removes the form, and nothing shows without a `ToastProvider` (the apply still works). |
155
+ | `CartAppliedCoupons` | `div` | Renders one chip per applied coupon | Reads `cart.appliedCoupons`; renders nothing when empty (no container). Each chip shows the `code` inline and a `CartCouponRemove`. An applied-but-no-discount coupon (`isUnmatchedCondition`) is exposed via the `data-fs-cart-coupon-unmatched` hook for the store to style/label (no baked copy); compose `CartCouponUnmatched` in the chip template for a text notice. A chip template replaces the built-in remove control, so one that composes the notice must also compose `CartCouponRemove`. |
156
156
  | `CartCouponRemove` | `button` | The per-chip remove control (the X) | Dispatches `$actions.cart.removeCoupon` for the chip's code. Use inside a `CartAppliedCoupons` chip, which supplies the code via context. |
157
157
  | `CartCouponError` | `div` (`role="alert"`) | Inline coupon error banner | Reads a **dedicated** coupon error channel, separate from `CartError` (which carries item-write errors). Renders the first error, or nothing when there are none. |
158
+ | `CartCouponEmpty` | `div` | Coupon entry gate | Renders its children only while the resolved cart has no applied coupon; renders nothing while the cart loads or once any coupon (matched or not) is applied. Copy-free, no schema props. Wrap the coupon entry in it for a single-coupon store; omit it for a multi-coupon store. |
159
+ | `CartCouponUnmatched` | `output` | Per-chip notice for an unmatched coupon | Reads the chip's coupon from context; renders the `message` copy only while that coupon has `isUnmatchedCondition`, and nothing when it matches, outside a chip, or when the copy is blank. `children` override `message`. Never removes the coupon. Not part of the built-in chip: compose it next to `CartCouponRemove`. |
160
+ | `CartCouponSavings` | `span` | Per-chip amount a fixed-amount coupon took off | Reads the chip's coupon from context and sums the `COUPON` entries of `cart.discounts` named after its code; renders the amount with the currency's own minus sign (`-R$ 20,00`), and nothing for an unmatched, free-shipping or percentage coupon (the API attributes no amount to them), outside a chip, or when the sum is not above zero. `template` words the text (default `{amount}`): every `{amount}` is the formatted saving and every `{code}` the chip's code; other `{…}` text stays literal, and a blank template counts as unset. The chip already shows the code before its children, so `{code}` repeats it. Not part of the built-in chip: compose it next to `CartCouponRemove`. |
158
161
 
159
- `CartAppliedCoupons` repeats its `children` once per applied coupon (like `CartList` repeats per item): place the chip template — an optional code element plus a `CartCouponRemove` — as its children, and it is cloned per coupon with the coupon supplied via context. Omit `children` to get a built-in chip (code inline + a remove X).
162
+ `CartAppliedCoupons` repeats its `children` once per applied coupon (like `CartList` repeats per item): place the chip template — an optional code element plus a `CartCouponRemove` — as its children, and it is cloned per coupon with the coupon supplied via context. Omit `children` to get a built-in chip (code inline + a remove X). A template that composes `CartCouponUnmatched` or `CartCouponSavings` must also compose `CartCouponRemove`, because composed children replace the built-in remove control.
160
163
 
161
- **Errors:** coupon apply/remove `userErrors` (e.g. `COUPON_NOT_FOUND`, `COUPON_EXPIRED`) publish to a dedicated reactive var that only `CartCouponError` reads, so coupon feedback never collides with item-write errors shown by `CartError`. On a single-coupon store, a rejected second code surfaces here as a normal apply error.
164
+ **Errors:** coupon apply/remove `userErrors` (e.g. `COUPON_NOT_FOUND`, `COUPON_EXPIRED`) publish to a dedicated reactive var that only `CartCouponError` reads, so coupon feedback never collides with item-write errors shown by `CartError`. A code rejected while another coupon is applied returns `COUPON_NOT_COMBINED` (whatever the account allows) with a neutral message, and the applied coupons stay; a store can override the text with `CartCouponError` children. `cartCouponErrorsVar` is cleared when a coupon write starts (item writes and `validate` leave it alone) and when a CartRoot mounts, never on re-render, so a rejected code from a layout mini-cart form does not show under the Cart page's coupon form. Two cases stay:
162
165
 
163
- **Styling split:** these components own behaviour and the `data-fs-*` hooks (`data-fs-cart-coupon-form`, `-input`, `-apply`, `-pending`, `data-fs-cart-applied-coupons`, `data-fs-cart-coupon`, `-code`, `-unmatched`, `data-fs-cart-coupon-remove`, `data-fs-cart-coupon-error`). Chip and X **visuals (swatch values) are authored by the styling pipeline**, not these components.
166
+ - A coupon apply still in flight when a CartRoot mounts shows its failure after the clear, for example from a layout mini-cart form (known limitation).
167
+ - An error from the Cart page's form stays visible in a persistent `CartCouponError` after the shopper leaves (known limitation).
168
+
169
+ **Styling split:** these components own behaviour and the `data-fs-*` hooks (`data-fs-cart-coupon-form`, `-input`, `-apply`, `-pending`, `data-fs-cart-applied-coupons`, `data-fs-cart-coupon`, `-code`, `-unmatched`, `data-fs-cart-coupon-remove`, `data-fs-cart-coupon-unmatched-notice`, `data-fs-cart-coupon-savings`, `data-fs-cart-coupon-error`, `data-fs-cart-coupon-empty`). Chip and X **visuals (swatch values) are authored by the styling pipeline**, not these components.
164
170
 
165
171
  Coupon block inside a mini-cart `DrawerPopup` footer, at the top of the footer above the totals and checkout CTA (the footer lives inside the `CartFilled` branch — coupons only matter for a non-empty cart):
166
172
 
@@ -168,13 +174,66 @@ Coupon block inside a mini-cart `DrawerPopup` footer, at the top of the footer a
168
174
  {
169
175
  "popup-footer": { "type": "Container", "children": ["coupon-block", "footer-summary", "checkout-cta", "footer-keep-shopping"] },
170
176
  "coupon-block": { "type": "Container", "children": ["coupon-form", "applied-coupons", "coupon-error"] },
171
- "coupon-form": { "type": "CartCouponForm", "props": { "placeholder": "Coupon code", "applyLabel": "Apply" } },
177
+ "coupon-form": { "type": "CartCouponForm", "props": { "placeholder": "Coupon code", "applyLabel": "Apply", "appliedMessage": "Coupon applied" } },
178
+ "applied-coupons": { "type": "CartAppliedCoupons", "children": ["coupon-remove"] },
179
+ "coupon-remove": { "type": "CartCouponRemove" },
180
+ "coupon-error": { "type": "CartCouponError" }
181
+ }
182
+ ```
183
+
184
+ ### Single- and multi-coupon entry
185
+
186
+ Whether a store accepts one coupon or several is expressed by composition, with no flag. For a single-coupon store, wrap the collapsed entry (the accordion holding `CartCouponForm`) in `CartCouponEmpty`: the trigger and form disappear as soon as a code is applied and come back, collapsed, when the chip is removed. For a multi-coupon store, leave the gate out so the entry stays available for more codes, each shown as its own chip. `CartAppliedCoupons` and `CartCouponError` sit outside the gate in both compositions, so the chips and errors stay visible.
187
+
188
+ Single-coupon:
189
+
190
+ ```json
191
+ {
192
+ "coupon-section": { "type": "Container", "children": ["coupon-entry-gate", "applied-coupons", "coupon-error"] },
193
+ "coupon-entry-gate": { "type": "CartCouponEmpty", "children": ["coupon-accordion"] },
194
+ "coupon-accordion": { "type": "AccordionRoot", "children": ["coupon-accordion-item"] },
195
+ "applied-coupons": { "type": "CartAppliedCoupons", "children": ["coupon-remove"] },
196
+ "coupon-remove": { "type": "CartCouponRemove" },
197
+ "coupon-error": { "type": "CartCouponError" }
198
+ }
199
+ ```
200
+
201
+ Chip template with the saved amount and the unmatched-coupon notice (compose `CartCouponRemove` too, since composed children replace the built-in remove):
202
+
203
+ ```json
204
+ {
205
+ "applied-coupons": { "type": "CartAppliedCoupons", "children": ["coupon-savings", "coupon-remove", "coupon-unmatched"] },
206
+ "coupon-savings": { "type": "CartCouponSavings", "props": { "template": "Economia de {amount}" } },
207
+ "coupon-remove": { "type": "CartCouponRemove", "props": { "children": "×" } },
208
+ "coupon-unmatched": { "type": "CartCouponUnmatched", "props": { "message": "Este cupom não está sendo aplicado" } }
209
+ }
210
+ ```
211
+
212
+ Multi-coupon (no gate):
213
+
214
+ ```json
215
+ {
216
+ "coupon-section": { "type": "Container", "children": ["coupon-accordion", "applied-coupons", "coupon-error"] },
217
+ "coupon-accordion": { "type": "AccordionRoot", "children": ["coupon-accordion-item"] },
172
218
  "applied-coupons": { "type": "CartAppliedCoupons", "children": ["coupon-remove"] },
173
219
  "coupon-remove": { "type": "CartCouponRemove" },
174
220
  "coupon-error": { "type": "CartCouponError" }
175
221
  }
176
222
  ```
177
223
 
224
+ `coupon-accordion-item` is the usual `AccordionItem > AccordionHeader > AccordionTrigger` plus `AccordionPanel > CartCouponForm`:
225
+
226
+ ```json
227
+ {
228
+ "coupon-accordion-item": { "type": "AccordionItem", "props": { "value": "coupon" }, "children": ["coupon-accordion-header", "coupon-accordion-panel"] },
229
+ "coupon-accordion-header": { "type": "AccordionHeader", "children": ["coupon-accordion-trigger"] },
230
+ "coupon-accordion-trigger": { "type": "AccordionTrigger", "children": ["coupon-accordion-trigger-label"] },
231
+ "coupon-accordion-trigger-label": { "type": "Text", "props": { "as": "span", "children": "Have a coupon?" } },
232
+ "coupon-accordion-panel": { "type": "AccordionPanel", "children": ["coupon-form"] },
233
+ "coupon-form": { "type": "CartCouponForm" }
234
+ }
235
+ ```
236
+
178
237
  ---
179
238
 
180
239
  ## Canonical schema example
@@ -230,7 +289,7 @@ Neither leaf renders a wrapper or carries spacing, so **order, grouping, and any
230
289
  Continue-shopping is a **non-mutating** control, not a cart action. Which control depends on the surface:
231
290
 
232
291
  - **Inside the mini-cart drawer**, use `CartContinueShopping` — it closes the drawer through the drawer's own contract and dispatches no cart write.
233
- - **On a cart or checkout page** (no drawer to close), use a storefront `NavigationLink` (e.g. `href="/"`) back to browsing. Do **not** place `CartContinueShopping` there: it is a drawer close, and without a `DrawerRoot` ancestor the render fails.
292
+ - **On a cart or checkout page** (no drawer to close), use a storefront `NavigationLink` (e.g. `href={{ page: 'home' }}`) back to browsing. Do **not** place `CartContinueShopping` there: it is a drawer close, and without a `DrawerRoot` ancestor the render fails.
234
293
 
235
294
  ```text
236
295
  CartRoot (layout)
@@ -259,7 +318,7 @@ CartRoot (layout)
259
318
  "popup-body": { "type": "CartList", "children": ["cart-item-template"] },
260
319
  "subtotal-row": { "type": "CartTotal", "props": { "label": "subtotal" } },
261
320
  "total-row": { "type": "CartTotal", "props": { "label": "total" } },
262
- "checkout-btn": { "type": "CartGoCheckout", "props": { "href": "/checkout", "children": "continuar" } },
321
+ "checkout-btn": { "type": "CartGoCheckout", "props": { "href": { "standard": "checkout" }, "children": "continuar" } },
263
322
 
264
323
  "cart-empty": { "type": "CartEmpty", "children": ["empty-illustration", "empty-msg", "empty-footer"] },
265
324
  "empty-illustration": { "type": "Icon", "props": { "name": "shopping-bag" } },
@@ -275,7 +334,7 @@ On a cart/checkout page there is no drawer to close, so the empty branch keeps `
275
334
  {
276
335
  "cart-empty": { "type": "CartEmpty", "children": ["empty-msg", "keep-shopping-link"] },
277
336
  "empty-msg": { "type": "CartEmptyMessage", "props": { "text": "sua cesta está vazia" } },
278
- "keep-shopping-link": { "type": "NavigationLink", "props": { "href": "/", "children": "continuar comprando" } }
337
+ "keep-shopping-link": { "type": "NavigationLink", "props": { "href": { "page": "home" }, "children": "continuar comprando" } }
279
338
  }
280
339
  ```
281
340
 
@@ -307,7 +366,7 @@ The mini-cart drawer (the layout composition described on `CartRoot`) ends in a
307
366
 
308
367
  - **`CartCheckoutGuard`** is the outer wrapper. It reads cart availability and, while any line item is not `AVAILABLE`, makes its whole subtree non-interactive (`inert` + `aria-disabled`) and exposes `data-fs-cart-checkout-blocked` for styling the disabled state. The shopper cannot proceed with an undeliverable item. It updates reactively from cache — no refetch, no reload — so the block clears the moment the offending line is removed or becomes available.
309
368
  - **`CartTotal`** sits inside the guard, on or next to the button, so the grand total the shopper is about to pay is visible at the CTA. It reads the nearest `CartRoot`/`CartTotalizerRoot` and reformats reactively on every cart change.
310
- - **`CartGoCheckout`** is the link to checkout (default `href` `/checkout`). During a **client-side** route transition it surfaces a loading state on its content wrapper — `aria-busy` plus `data-fs-cart-go-checkout-pending` — so the shopper sees the click registered. There is **no built-in spinner**: opt into one by styling that attribute (or composing a `Spinner` on it). For an **external or hard** checkout navigation (a non-Next route, a different origin, or a full page load — common for the real VTEX checkout) there is no client-side transition window, so the loading state simply does not show. That is the documented, graceful no-op, not a bug.
369
+ - **`CartGoCheckout`** is the link to checkout (default `href` the checkout page, `{ "standard": "checkout" }`). During a **client-side** route transition it surfaces a loading state on its content wrapper — `aria-busy` plus `data-fs-cart-go-checkout-pending` — so the shopper sees the click registered. There is **no built-in spinner**: opt into one by styling that attribute (or composing a `Spinner` on it). For an **external or hard** checkout navigation (a non-Next route, a different origin, or a full page load — common for the real VTEX checkout) there is no client-side transition window, so the loading state simply does not show. That is the documented, graceful no-op, not a bug.
311
370
 
312
371
  **The button wording is CMS content.** It resolves in a fixed order: the `cart-checkout-label` entry, then the `label` prop, then `children`, then the package default `"Checkout"`. So a merchant rewords the CTA — `"Finalizar compra"`, `"Ir para o pagamento"` — with a CMS save and no deployment, while a store with no CMS binding renders its authored value. `children` stays supported because that is where stores labelled the button before `label` existed; a CMS entry overrides it with no schema edit. A blank or whitespace-only entry value counts as unconfigured and falls through. Non-compiler hosts (Storybook, tests) pass a typed `$content` fixture directly.
313
372
 
@@ -315,7 +374,7 @@ The mini-cart drawer (the layout composition described on `CartRoot`) ends in a
315
374
 
316
375
  | Prop | Type | Required | Notes |
317
376
  |-------|------|----------|-------|
318
- | `href` | string | no | Checkout destination, default `/checkout`. Never content-editable |
377
+ | `href` | page reference or string | no | Checkout destination, default `{ "standard": "checkout" }`. A string is a logical path or an off-site URL. Never content-editable |
319
378
  | `label` | string | no | Authored fallback for the wording. Ignored when an entry supplies a label |
320
379
  | `children` | node | no | Legacy label channel, still honoured. Element children are replaced when an entry supplies a label |
321
380
 
@@ -332,7 +391,7 @@ Schema shape (inside the `DrawerPopup` footer `Container`):
332
391
  "popup-footer": { "type": "Container", "children": ["checkout-cta"] },
333
392
  "checkout-cta": { "type": "CartCheckoutGuard", "children": ["checkout-total", "checkout-btn"] },
334
393
  "checkout-total": { "type": "CartTotal", "props": { "label": "Total" } },
335
- "checkout-btn": { "type": "CartGoCheckout", "props": { "href": "/checkout", "label": "Finalizar compra" } }
394
+ "checkout-btn": { "type": "CartGoCheckout", "props": { "href": { "standard": "checkout" }, "label": "Finalizar compra" } }
336
395
  }
337
396
  ```
338
397
 
@@ -342,11 +401,34 @@ Everything stays a layout composition: the guard, total, and link are independen
342
401
 
343
402
  ---
344
403
 
404
+ ## Item count
405
+
406
+ `CartCount` renders `<span data-fs-cart-count>` with the number of units in the cart: the sum of `quantity` over the non-gift lines (two lines of 1 read 2; one line of 3 reads 3; a gift-only cart reads 0). Must be inside `CartRoot`.
407
+
408
+ - `template` — the output format, `{count}` replaced by the count; default `'{count}'`. Children override the text.
409
+ - `templateOne` — the output format used **only when the count is exactly 1**. It has no default: when unset, or blank, `template` is used at 1 as well. **0 uses `template`**, never `templateOne`, because the selector is the literal count and not a locale plural rule (in `pt-BR` the plural rule would call 0 singular).
410
+ - A count that is not a finite number renders **nothing**. A non-gift line whose `quantity` is missing, `null`, `NaN`, infinite or a string makes the count unreliable, so the span is not rendered, even with `children`; a partial sum would undercount. 0 is finite: an empty or gift-only cart still renders `0`.
411
+
412
+ `CartUnavailableCount` takes the same `template` and `templateOne` pair (see [below](#unavailable-item-recovery)), selected on the number of unavailable lines.
413
+
414
+ ```json
415
+ {
416
+ "cart-title-count": {
417
+ "type": "CartCount",
418
+ "props": { "template": "{count} itens", "templateOne": "{count} item" }
419
+ }
420
+ }
421
+ ```
422
+
423
+ Only the first `{count}` in a template is replaced.
424
+
425
+ ---
426
+
345
427
  ## Unavailable-item recovery
346
428
 
347
429
  `CartCheckoutGuard` blocks checkout while any line is not `AVAILABLE`; the recovery affordances let the shopper resolve the block from inside the cart. Three pieces, all reading the availability already on the cart:
348
430
 
349
- - **`CartUnavailableCount`** — cart-level summary of how many lines are unavailable (e.g. "2 items unavailable"). Renders a `<span data-fs-cart-unavailable-count>` with a `template` prop (`{count}` is replaced by the integer count; default `'{count} items unavailable'`; children override the text). **Self-hides** (`null`) when every line is available — including while the cart is still loading — so it never shows a false "0 items unavailable". Must be inside `CartRoot`.
431
+ - **`CartUnavailableCount`** — cart-level summary of how many lines are unavailable (e.g. "2 items unavailable"). Renders a `<span data-fs-cart-unavailable-count>` with a `template` prop (`{count}` is replaced by the integer count; default `'{count} items unavailable'`; children override the text) and an optional `templateOne` used only when exactly one line is unavailable (it falls back to `template` when unset or blank). **Self-hides** (`null`) when every line is available — including while the cart is still loading — so it never shows a false "0 items unavailable". Must be inside `CartRoot`.
350
432
  - **`CartRemoveUnavailable`** — the bulk "remove all unavailable" button (`<button data-fs-cart-remove-unavailable>`). On click it dispatches **one** `$actions.cart.removeItems({ ids })` call carrying every unavailable line id: one mutation, one upstream write, one cache update — no per-line flicker. **Self-hides** when there is nothing to remove. Children are the label (default "Remove unavailable items"). Must be inside `CartRoot` (it reads the cart for the ids; the write itself is context-free).
351
433
  - **Per-item remove-on-alert** — a `CartItemRemove` nested inside `CartItemUnavailable` in the line-item template, so each unavailable alert carries its own remove control. Composition only, no new component — see `cart-item.md` (including the label-vs-children nuance: children replace the built-in label).
352
434
 
@@ -365,6 +447,31 @@ Schema shape (mini-cart footer, above the checkout CTA):
365
447
 
366
448
  ---
367
449
 
450
+ ## Clearing the cart
451
+
452
+ `ClearCartButton` empties the cart in one click (`<button data-fs-clear-cart-button>`). It reads every line id from the cart and dispatches **one** `$actions.cart.removeItems({ ids })` call: every line goes, gift and unavailable lines included, in cart order. No confirmation dialog and no toast. Children are the label (default "Clear cart"). Must be inside `CartRoot`.
453
+
454
+ - **Self-hides** (`null`) when the cart has no lines or has not resolved, as `CartRemoveUnavailable` does.
455
+ - **Disabled while pending.** The button carries the native `disabled` attribute plus `data-fs-clear-cart-button-pending` until the call settles, and a second click in the same tick is swallowed, so one click is one mutation. Style the pending look with `disabled:` classes or the pending hook. A caller `disabled` prop also disables it.
456
+ - **Optimistic.** `removeItems` writes an optimistic cart, so `CartEmpty` replaces `CartFilled` as soon as the button is clicked, before the server answers. The button itself unmounts at that point when it sits inside `CartFilled`.
457
+ - **Failures go through `CartError`.** When the call fails, Apollo drops the optimistic layer, the lines come back, and the failure is published to the same `cartUserErrorsVar` that `CartError` shows. The button owns no rollback and raises no toast. `cartUserErrorsVar` is cleared when an item write starts (coupon writes and `validate` leave it alone) and when a CartRoot mounts, never on re-render, so an error from a write on another page does not show on the Cart page. Two cases stay, listed below. A rejection (no cart cached yet) is logged by error name and re-enables the button. Compose a `CartError` outside the `CartFilled` / `CartEmpty` branches so the message is visible after the lines return.
458
+ - A write still in flight when a CartRoot mounts shows its failure after the clear, for example an item write from a product page (known limitation).
459
+ - An error from the Cart page stays visible in a persistent `CartError` after the shopper leaves (known limitation).
460
+
461
+ Schema shape (Cart page):
462
+
463
+ ```json
464
+ {
465
+ "page-shell": { "type": "Container", "children": ["page-heading", "cart-error", "page-body", "cart-loading", "cart-empty"] },
466
+ "cart-error": { "type": "CartError" },
467
+ "page-body": { "type": "CartFilled", "children": ["clear-cart-row"] },
468
+ "clear-cart-row": { "type": "Container", "children": ["clear-cart-btn"] },
469
+ "clear-cart-btn": { "type": "ClearCartButton", "props": { "children": "Limpar carrinho" } }
470
+ }
471
+ ```
472
+
473
+ ---
474
+
368
475
  ## Merchant alert banner
369
476
 
370
477
  `CartAlertRoot` + `CartAlertIcon` + `CartAlertText` carry the **merchant's own** message inside the overlay — a minimum order, a running promotion, a policy note. The copy is CMS-editable, so changing it needs no schema edit and no deploy.
@@ -421,7 +528,7 @@ The message, as `<span data-fs-cart-alert-text data-fs-text>`. Copy comes from t
421
528
 
422
529
  ### The `icons.include` obligation
423
530
 
424
- `Icon` resolves names against a **compile-time** sprite with no runtime fallback. An `icon` **prop** is a literal the compiler can see, so it is collected automatically. A **CMS-chosen** name does not exist at build time, so a store whose merchants pick the icon must register the admitted names in `global.json`:
531
+ `Icon` resolves names against a **compile-time** sprite with no runtime fallback. An `icon` **prop** is a literal the compiler can see, so it is collected automatically. A **CMS-chosen** name does not exist at build time, so a store whose merchants pick the icon must register the admitted names in `foundation.json`:
425
532
 
426
533
  ```json
427
534
  { "icons": { "include": ["bell", "bell-ringing", "tag", "shopping-cart", "map-pin", "star"] } }
@@ -1,7 +1,7 @@
1
1
  # ClientProfileFieldset
2
2
 
3
3
  **Category:** identity
4
- **Purpose:** The composable, one-Save write of the `ClientProfile` entity. Registers itself with the enclosing `FormRoot` under the `clientProfile` namespace and dispatches at most one `identity.clientProfileUpdate` when the shopper presses that form's submit. Four bound controls — first name, last name, phone, document — compose freely inside it, in any order or subset.
4
+ **Purpose:** The composable, one-Save write of the `ClientProfile` entity. Registers itself with the enclosing `FormRoot` under the `clientProfile` namespace and dispatches at most one `identity.clientProfileUpdate` when the shopper presses that form's submit. Four bound controls — first name, last name, phone, document (CPF, SSN, SIN or RFC) — compose freely inside it, in any order or subset.
5
5
 
6
6
  ---
7
7
 
@@ -65,7 +65,7 @@ SessionRoot (required ancestor; session tri-state +
65
65
  | Prop | Type | Default | Notes |
66
66
  |------|------|---------|-------|
67
67
  | `country` | `'US' \| 'BR'` | `'US'` | Phone mask/validation country, provided to the phone control via context. |
68
- | `documentType` | string | — | e.g. `'CPF'`. The document control renders only when supported. |
68
+ | `documentType` | string | — | One of `'CPF'`, `'SSN'`, `'SIN'` or `'RFC'` (upper case). The document control renders only when supported, and the payload carries `document` only for a supported type. |
69
69
  | `skeletonCount` | number | `4` | Placeholder rows drawn while the profile resolves. Set it to the number of controls composed. |
70
70
  | `loadingLabel` | string | `'Loading your details'` | Announced politely while the profile resolves. Blank means no announcement. |
71
71
  | `loadedLabel` | string | `'Your details are ready'` | Announced politely once the controls mount. Blank means no announcement. |
@@ -105,12 +105,14 @@ governs visibility (see *Switching the phone field off* below).
105
105
 
106
106
  | Prop | Type | Default | Notes |
107
107
  |------|------|---------|-------|
108
- | `label` | string | `'CPF'` | |
108
+ | `label` | string | the document type's name (`'CPF'`, `'SSN'`, `'SIN'` or `'RFC'`) | |
109
109
  | `placeholder` | string | — | |
110
110
  | `errorMessage` | string | — | |
111
111
 
112
- Renders nothing, and registers no field name, when the fieldset's `documentType` is unsupported. Composes
113
- the `DocumentField` particle. Recomposable (RFC:503): given children, they replace `DocumentField`'s
112
+ Renders nothing, and registers no field name, when the fieldset's `documentType` is unsupported. The mask,
113
+ the validation and the `autocomplete` hint (`off` for SSN and SIN) come from the document type; the saved
114
+ `document` string is the type's sanitised value (digits for CPF, SSN and SIN, letters kept for RFC), the
115
+ same string the card form sends. Composes the `DocumentField` particle. Recomposable (RFC:503): given children, they replace `DocumentField`'s
114
116
  default `Field.Label` + `DocumentFieldInput` + `Field.Error` anatomy, in any order; childless keeps
115
117
  today's order and text.
116
118
 
@@ -0,0 +1,84 @@
1
+ # CopyText
2
+
3
+ **Category:** action
4
+ **Purpose:** Copies one string the caller supplies and confirms the result inline.
5
+
6
+ ---
7
+
8
+ ## Visual cues (how to identify in an image)
9
+
10
+ A small action beside an identifier, with a short confirmation that appears after the action and can be dismissed. The image is the source of truth for layout.
11
+
12
+ ## When to use
13
+
14
+ Any screen that needs one action to copy a single string, such as an order number.
15
+
16
+ ## When NOT to use
17
+
18
+ - Rendering the identifier itself. Pass the string in; do not ask `CopyText` to display the heading.
19
+ - A toast or a page-level notification. The confirmation stays with the action.
20
+
21
+ ---
22
+
23
+ ## Composition
24
+
25
+ ```
26
+ CopyText
27
+ ```
28
+
29
+ `CopyText` is one element. The caller passes the visible label or icon as `children`.
30
+
31
+ ## Components
32
+
33
+ ### CopyText
34
+
35
+ | Prop | Type | Default | Purpose |
36
+ |---|---|---|---|
37
+ | `accessibleName` | string | `Copy` | Accessible name of the action |
38
+ | `successMessage` | string | `Copied` | Announced after a successful copy |
39
+ | `failureMessage` | string | `Could not copy` | Announced when the clipboard rejects the write |
40
+ | `dismissLabel` | string | `Dismiss` | Accessible name of the dismiss control |
41
+
42
+ `text` is required and is not a schema prop. The caller supplies the exact string to copy. `children` is the visible label or icon, and it is not a schema prop either.
43
+
44
+ ---
45
+
46
+ ## Canonical schema example
47
+
48
+ ```json
49
+ {
50
+ "copy-identifier": {
51
+ "type": "CopyText",
52
+ "props": {
53
+ "accessibleName": "Copy order number",
54
+ "successMessage": "Copied",
55
+ "failureMessage": "Could not copy",
56
+ "dismissLabel": "Dismiss"
57
+ },
58
+ "children": []
59
+ }
60
+ }
61
+ ```
62
+
63
+ The screen that owns the identifier passes `text` and `children` at runtime. Do not author the identifier in the page.
64
+
65
+ ## Common mistakes
66
+
67
+ - **OrderDetailsHeader** renders the number. `CopyText` only copies the string it is given.
68
+ - Passing the heading, including a prefix such as "Order #", copies that whole string. Pass the identifier alone.
69
+
70
+ ---
71
+
72
+ ## Styling
73
+
74
+ | Component | What to style | Plausible classes |
75
+ |---|---|---|
76
+ | `CopyText` | The action and the inline confirmation | `inline-flex items-center gap-[8px] text-[14px]` |
77
+
78
+ Style the hooks `data-fs-copy-text`, `data-fs-copy-text-action`, `data-fs-copy-text-message`, and `data-fs-copy-text-outcome`. The component ships no CSS.
79
+
80
+ ---
81
+
82
+ ## Related
83
+
84
+ - [[button]] — the action primitive this control composes.
@@ -1,14 +1,14 @@
1
1
  # DocumentField
2
2
 
3
3
  **Category:** content
4
- **Purpose:** A presentational tax-document input that composes the `Field` particle (label + native `<input>` + inline error). It applies a locale-specific mask on every change (e.g. CPF `###.###.###-##`) and runs check-digit validation on blur/submit. It is data-agnostic — value flows through props; it consumes no GraphQL and no domain context.
4
+ **Purpose:** A presentational tax-document input that composes the `Field` particle (label + native `<input>` + inline error). It applies the mask of its document type on every change (CPF `###.###.###-##`, SSN `###-##-####`, SIN `###-###-###`, or RFC, thirteen upper-case letters and digits) and runs that type's validation on blur/submit. It is data-agnostic — value flows through props; it consumes no GraphQL and no domain context.
5
5
 
6
6
  ---
7
7
 
8
8
  ## Visual cues (how to identify in an image)
9
9
 
10
- - A single-line text input labelled **"CPF"** (or another tax-document name), usually on a checkout identification step, near the email/phone fields.
11
- - Shows a masked placeholder like `000.000.000-00` and formats digits as they are typed.
10
+ - A single-line text input labelled **"CPF"**, **"SSN"**, **"SIN"** or **"RFC"** (or another tax-document name), usually on a checkout identification step, near the email/phone fields.
11
+ - Shows a masked placeholder like `000.000.000-00` (CPF), `000-00-0000` (SSN), `000-000-000` (SIN) or `AAAA000000AAA` (RFC) and formats characters as they are typed.
12
12
  - When invalid, an inline error below the input (e.g. *"Enter a valid CPF."*).
13
13
 
14
14
  If the image shows a checkout identification step with a tax-document field, use `DocumentField`.
@@ -61,15 +61,15 @@ react-hook-form and loses the mask — unsupported, the RFC's named double-binde
61
61
  | `label` | string | — | Accessible label. |
62
62
  | `placeholder` | string | — | Input placeholder. |
63
63
  | `disabled` | boolean | `false` | Disables the input (e.g. while a write is in flight). |
64
- | `requirement` | `'mandatory' \| 'optional'` | — | Raises the field to required, announced as `aria-required` (never the native attribute). Unset is no opinion, which is what keeps CPF Optional by Recommendation, unlike email. Bound to a fieldset whose rule already says required, `'optional'` does not lower it. |
64
+ | `requirement` | `'mandatory' \| 'optional'` | — | Raises the field to required, announced as `aria-required` (never the native attribute). Unset is no opinion, which is what keeps the document Optional by Recommendation, unlike email. Bound to a fieldset whose rule already says required, `'optional'` does not lower it. |
65
65
  | `name` | string | `'document'` | Field/input name. |
66
- | `documentType` | `'CPF'` | `'CPF'` | Selects the mask + validator + digit-only input mode. |
66
+ | `documentType` | `'CPF' \| 'SSN' \| 'SIN' \| 'RFC'` | `'CPF'` | Selects the mask, sanitiser, validator, digit-only input mode and `autocomplete`. `DOCUMENTS` holds one entry per type; the API's lower-case ids (`cpf`, `ssn`, `sin`, `rfc`) go through `documentTypeFromId`. |
67
67
  | `ref` | `Ref<HTMLInputElement>` | — | Lands on the `<input>` `Field.Control` renders — **not on the wrapper `<div>`, as it did before** (a deliberate, source-breaking change). Composed with react-hook-form's own ref on the bound path — a caller ref and react-hook-form's ref are both honoured. |
68
68
  | `children` | `ReactNode` | — | Given, replaces the default `Field.Label` + `DocumentFieldInput` + `Field.Error` anatomy, in any order (RFC:503). |
69
69
 
70
- **Masking:** applied progressively on every change; input is never blocked. Digit-only masks (CPF) strip non-digits and hint `inputMode="numeric"`.
70
+ **Masking:** applied progressively on every change; input is never blocked. The type's sanitiser runs first: digit-only types (CPF, SSN, SIN) strip non-digits and hint `inputMode="numeric"`; RFC upper-cases and keeps only `A-Z` and `0-9`, and carries no `inputMode`. SSN and SIN inputs carry `autocomplete="off"` so the browser does not offer the number back; CPF and RFC carry no `autocomplete` attribute. The attribute is a request, not a guarantee.
71
71
 
72
- **Validation:** the check digit is checked on blur/submit only — never per keystroke. An **empty** value is valid (the field is optional). A prior error clears while the shopper edits and is re-evaluated on the next blur.
72
+ **Validation:** the type's rule (CPF check digits; SSN area, group and serial ranges; SIN Luhn check; RFC structure with a plausible month and day) is checked on blur/submit only — never per keystroke. An **empty** value is valid (the field is optional). A prior error clears while the shopper edits and is re-evaluated on the next blur.
73
73
 
74
74
  ---
75
75
 
@@ -82,7 +82,7 @@ runs the plain, unbound behaviour documented above, unaltered.
82
82
  - **What the contract supplies**: `label` and `placeholder`, when the particle's own prop is omitted (an
83
83
  explicit prop always wins); `defaultValue`, seeded into `useController`; and `rule`, merged with the
84
84
  particle's own check-digit validator (below).
85
- - **What stays the particle's**: the CPF mask, `documentType`, and the `errorMessage` copy.
85
+ - **What stays the particle's**: the mask of the `documentType`, the `documentType` itself, and the `errorMessage` copy.
86
86
  - **Single binder, real contract for the read-only parts (RFC:503)**: the composite remains the sole
87
87
  binder for its field — `useController` is called once, here. Its default (and a recomposed) anatomy's
88
88
  input is `DocumentFieldInput`, which renders the raw Base UI `Field.Control` directly rather than this
@@ -58,7 +58,7 @@ It renders **both responsive forms at once**, each under its own hook: an accord
58
58
  | Prop | Type | Required | Notes |
59
59
  |-------|------|----------|-------|
60
60
  | `heading` | string | no | Authored fallback. Ignored when a CMS entry supplies a heading |
61
- | `links` | array | no | Authored fallback list of `{ label, href, target? }`. Ignored when a CMS entry supplies `links` |
61
+ | `links` | array | no | Authored fallback list of `{ label, href, target? }`. An item `href` may be a page reference or a logical path. Ignored when a CMS entry supplies `links` |
62
62
 
63
63
  Content model (`footer-link-group`) fields, edited in the CMS:
64
64
 
@@ -67,7 +67,7 @@ Content model (`footer-link-group`) fields, edited in the CMS:
67
67
  | `heading` | string | no | The group heading and the accordion trigger's accessible name. Optional so a links-only entry falls back to the authored heading instead of omitting the group. A group with no heading resolved from either channel renders nothing |
68
68
  | `links` | array | no | Ordered links. Reordering here reorders the rendered list with no deployment. Optional so a heading-only entry falls back to the authored list instead of omitting the group |
69
69
  | `links[].label` | string | yes | Link text. A blank label drops that link — an anchor with no name is unusable |
70
- | `links[].href` | string | yes | Destination. `http:`, `https:`, `mailto:`, `tel:`, or a path starting with a single `/` |
70
+ | `links[].href` | string | yes | A `uri-reference` string: `http:`, `https:`, `mailto:`, `tel:`, or a path starting with a single `/`. The binding prefix is applied at render |
71
71
  | `links[].target` | `_self` \| `_blank` | no | `_blank` opens a new tab and adds `rel="noopener noreferrer"` automatically |
72
72
 
73
73
  ---
@@ -52,7 +52,7 @@ FooterRoot
52
52
 
53
53
  **A destination the allowlist refuses renders as text.** Every `url` passes through the shared render-time check, which admits `http:`, `https:`, `mailto:`, `tel:`, and site-relative paths. A refused one — a `javascript:` value, a protocol-relative `//host`, or a URL not yet pasted — renders the profile's label as visible text with no link and no glyph, rather than a mark that looks clickable and goes nowhere. The rejection logs once with the entry index and a bounded reason.
54
54
 
55
- **Every predefined name must be registered.** A `platform` resolves to a sprite symbol the store's build has to include; the store's `global.json` writes the names out under `icons.include` — it is JSON, so it cannot import `SOCIAL_PLATFORM_NAMES`, and a test that derives the expected set from the exported constant is what keeps the two in step. A name the store never registered renders an empty `<svg>` — no build error and no runtime error, just a missing mark. `linkedin` and `youtube` need provisioned icon data as well: unlike the other three they have no embedded fallback, so a store that registers them without running `sukima icon-data fetch` fails to build.
55
+ **Every predefined name must be registered.** A `platform` resolves to a sprite symbol the store's build has to include; the store's `foundation.json` writes the names out under `icons.include` — it is JSON, so it cannot import `SOCIAL_PLATFORM_NAMES`, and a test that derives the expected set from the exported constant is what keeps the two in step. A name the store never registered renders an empty `<svg>` — no build error and no runtime error, just a missing mark. `linkedin` and `youtube` need provisioned icon data as well: unlike the other three they have no embedded fallback, so a store that registers them without running `sukima icon-data fetch` fails to build.
56
56
 
57
57
  ---
58
58
 
@@ -1,7 +1,7 @@
1
1
  # FreeShippingProgress
2
2
 
3
3
  **Category:** commerce
4
- **Purpose:** Reactive progress bar for the cart's nearest free-shipping goal. Reads `cart.freeShippingProgress` and the reactive `cart.total`, then renders one of three states: below-threshold ("faltam {value} para frete grátis" + a partially filled bar), earned ("você ganhou frete grátis!" + a full bar), or absent (renders nothing). Composed at the **top of a cart / mini-cart drawer** to nudge the shopper to add a bit more.
4
+ **Purpose:** Reactive progress bar for the cart's nearest free-shipping goal. Reads `cart.freeShippingProgress` and the cart's `Items` totalizer (the amount the server measures, so shipping and discounts do not count), then renders one of three states: below-threshold ("faltam {value} para frete grátis" + a partially filled bar), earned ("você ganhou frete grátis!" + a full bar), or absent (renders nothing). Composed at the **top of a cart / mini-cart drawer** to nudge the shopper to add a bit more.
5
5
 
6
6
  ---
7
7
 
@@ -18,19 +18,23 @@ Look for a **strip at the top of an open cart or mini-cart** with a horizontal p
18
18
 
19
19
  | State | Condition | Renders |
20
20
  |---|---|---|
21
- | Below threshold | `qualified === false`, cart total below `threshold` | Below-threshold message + a bar filled to `cartTotal / threshold` |
22
- | Earned | field `qualified === true`, OR the reactive cart total reaches `threshold` | Earned message + a full bar |
21
+ | Below threshold | `qualified === false`, `Items` amount below `threshold` | Below-threshold message + a bar filled to `items / threshold` |
22
+ | Earned | field `qualified === true`, OR the reactive `Items` amount reaches `threshold` | Earned message + a full bar |
23
23
  | Absent (self-hide) | `cart.freeShippingProgress === null` (no destination, no eligible promotion, empty cart, still loading, or degraded) | **Nothing** — no bar, no message, no wrapper |
24
24
 
25
25
  ## Reactive fill (no extra calls)
26
26
 
27
- `freeShippingProgress` is a lazy field kept out of `CartFragment`, so its cached value (`threshold`, `qualified`) does not refresh on cart mutations. The bar therefore derives the **live** fill and remaining from the reactive `cart.total` — which *is* fresh via cache-write propagation — against the fetched `threshold`:
27
+ `freeShippingProgress` is kept out of `CartFragment`, so only some writes select it: the four line writes (remove, remove several, increase, decrease), the coupon removal and the shipping address update. Each returns the field and the bar takes the server's answer: below the goal it shows the amount still missing, and it comes back to qualified when a later write meets the goal again. The cart's `Items` totalizer is reactive too (the optimistic builders adjust it), so between a write's optimistic prediction and the server's answer the bar derives the **live** fill and remaining from the `Items` amount against the cached `threshold`. The server compares the same `Items` amount, so shipping charges and discounts never move the bar:
28
28
 
29
- - `isQualified = qualified || (threshold != null && cartTotal >= threshold)` — the field flag covers the already-earned case (where `threshold` is `null`); the reactive comparison flips it at the boundary between page loads.
30
- - `liveRemaining = isQualified ? 0 : max(threshold − cartTotal, 0)`
31
- - `fill = isQualified ? 1 : clamp(cartTotal / threshold, 0..1)`
29
+ - `isQualified = qualified || (threshold != null && items >= threshold)` — the field flag covers the already-earned case (where `threshold` is `null`); the reactive comparison flips it at the boundary.
30
+ - `liveRemaining = isQualified ? 0 : max(threshold − items, 0)`
31
+ - `fill = isQualified ? 1 : clamp(items / threshold, 0..1)`
32
32
 
33
- The bar updates instantly on every add/remove with **no** refetch of `freeShippingProgress`. `threshold` refreshes only on a full page re-sync. `Money.amount` is a string at runtime and is always `Number()`-coerced before math/formatting.
33
+ When the cart has no usable `Items` amount (no `Items` totalizer, or a blank or non-numeric amount), the bar shows the server's own answer instead: `isQualified = qualified`, `liveRemaining = max(remaining, 0)` from `freeShippingProgress.remaining`, and `fill = (threshold − liveRemaining) / threshold`.
34
+
35
+ While a line write that lowers the items total of a qualified cart is pending, the bar hides: a qualified value has no threshold, so the client cannot tell whether the drop crosses the goal. It returns with the server's answer, and also when the write fails. An emptied cart renders nothing.
36
+
37
+ `addItem`, coupon apply, validate, shipping option select, the region postal code and the cart shipping calculator do not select the field, so they do not refresh it: after one of those the bar keeps the value of the last write that returned it (or of the page load) and derives its state from the `Items` amount. A line write costs no extra read, because the server reuses the order form the write returned; a coupon removal costs one order-form read upstream when the cart has items and a postal code; the shipping address update costs none, because the server reuses the order form it just refreshed. There is no client refetch. `Money.amount` is a string at runtime and is always `Number()`-coerced before math/formatting.
34
38
 
35
39
  ---
36
40
 
@@ -58,7 +62,7 @@ FreeShippingProgressRoot ← reactive provider; self-hides on null/loadin
58
62
 
59
63
  | Component | Props | Notes |
60
64
  |---|---|---|
61
- | `FreeShippingProgressRoot` | `$data` | Provides `FreeShippingProgressContext`; the copy formats from the active storefront context; renders nothing when the field is `null` |
65
+ | `FreeShippingProgressRoot` | `threshold?`, `remaining?`, `qualified?`, `itemsTotal?`, `$data` | Provides `FreeShippingProgressContext`; the copy formats from the active storefront context; renders nothing when the field is `null` |
62
66
  | `FreeShippingProgressMessage` | `label?`, `qualifiedLabel?` | `label` is a `{value}`-token template (default `"faltam {value} para frete grátis"`); `qualifiedLabel` default `"você ganhou frete grátis!"`; `children` overrides the text |
63
67
  | `FreeShippingProgressBar` | — | Renders a `Meter` bar; `aria-valuetext` mirrors the visible copy |
64
68