@vtex/faststore 0.0.8 → 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 (151) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/actions.json +42 -0
  3. package/dist/catalog.json +731 -16
  4. package/dist/cli/index.js +1 -1
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/components.d.ts +9 -4
  7. package/dist/components.js +11 -6
  8. package/dist/content-types.json +300 -0
  9. package/dist/core/dist/define-component.d.ts +6 -1
  10. package/dist/docs/account-entry-point.md +4 -4
  11. package/dist/docs/account-menu.md +2 -2
  12. package/dist/docs/account-saved-cards.md +301 -0
  13. package/dist/docs/binding-switcher.md +154 -0
  14. package/dist/docs/card-entry.md +3 -3
  15. package/dist/docs/card-summary.md +175 -0
  16. package/dist/docs/cart-destination.md +238 -0
  17. package/dist/docs/cart-item.md +21 -3
  18. package/dist/docs/cart-totalizer.md +1 -1
  19. package/dist/docs/cart.md +118 -11
  20. package/dist/docs/footer-social.md +1 -1
  21. package/dist/docs/free-shipping-progress.md +13 -9
  22. package/dist/docs/incentives.md +2 -2
  23. package/dist/docs/logo.md +1 -1
  24. package/dist/docs/payment-method-badges.md +2 -2
  25. package/dist/docs/payment-methods.md +1 -1
  26. package/dist/docs/product-information.md +2 -2
  27. package/dist/docs/product-shelf.md +7 -0
  28. package/dist/docs/select.md +3 -4
  29. package/dist/docs/sign-in-redirect.md +1 -1
  30. package/dist/docs/toast.md +30 -12
  31. package/dist/index.d.ts +2 -1
  32. package/dist/internal/client.d.ts +4 -1
  33. package/dist/internal/client.js +4 -1
  34. package/dist/internal/server.d.ts +4 -2
  35. package/dist/internal/server.js +4 -2
  36. package/dist/matter/account-saved-cards/account-saved-cards.d.ts +383 -0
  37. package/dist/matter/account-saved-cards/account-saved-cards.js +956 -0
  38. package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
  39. package/dist/matter/billing-address/billing-address.js.map +1 -1
  40. package/dist/matter/cart/cart-namespace.d.ts +5 -1
  41. package/dist/matter/cart/cart-namespace.js +5 -1
  42. package/dist/matter/cart/cart-namespace.js.map +1 -1
  43. package/dist/matter/cart/cart.d.ts +34 -4
  44. package/dist/matter/cart/cart.js +238 -28
  45. package/dist/matter/cart/cart.js.map +1 -1
  46. package/dist/matter/cart-destination/cart-destination.d.ts +222 -0
  47. package/dist/matter/cart-destination/cart-destination.js +613 -0
  48. package/dist/matter/cart-destination/cart-destination.js.map +1 -0
  49. package/dist/matter/cart-item/cart-item.d.ts +4 -2
  50. package/dist/matter/cart-item/cart-item.js +75 -16
  51. package/dist/matter/cart-item/cart-item.js.map +1 -1
  52. package/dist/matter/cart-totalizer/cart-totalizer.js +3 -2
  53. package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
  54. package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +6 -3
  55. package/dist/matter/free-shipping-progress/free-shipping-progress.js +76 -33
  56. package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
  57. package/dist/matter/pricing/pricing.js.map +1 -1
  58. package/dist/matter/product-shelf/product-shelf.d.ts +7 -0
  59. package/dist/matter/product-shelf/product-shelf.js +115 -1
  60. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  61. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  62. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +1 -1
  63. package/dist/matter/shipping-simulator/shipping-simulator.js +6 -2
  64. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  65. package/dist/particles/binding-switcher/binding-switcher.d.ts +38 -0
  66. package/dist/particles/binding-switcher/binding-switcher.js +183 -0
  67. package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
  68. package/dist/particles/card-summary/card-network.d.ts +17 -0
  69. package/dist/particles/card-summary/card-network.js +25 -0
  70. package/dist/particles/card-summary/card-network.js.map +1 -0
  71. package/dist/particles/card-summary/card-summary.d.ts +83 -0
  72. package/dist/particles/card-summary/card-summary.js +188 -0
  73. package/dist/particles/card-summary/card-summary.js.map +1 -0
  74. package/dist/particles/footer-social/footer-social.d.ts +1 -1
  75. package/dist/particles/footer-social/footer-social.js +1 -1
  76. package/dist/particles/footer-social/footer-social.js.map +1 -1
  77. package/dist/particles/incentives/incentives.d.ts +1 -1
  78. package/dist/particles/incentives/incentives.js +1 -1
  79. package/dist/particles/incentives/incentives.js.map +1 -1
  80. package/dist/particles/payment-methods/payment-methods.d.ts +1 -1
  81. package/dist/particles/payment-methods/payment-methods.js +1 -1
  82. package/dist/particles/payment-methods/payment-methods.js.map +1 -1
  83. package/dist/particles/toast/toast.d.ts +20 -6
  84. package/dist/particles/toast/toast.js +49 -7
  85. package/dist/particles/toast/toast.js.map +1 -1
  86. package/dist/schema.graphql +298 -34
  87. package/dist/sdk/actions/index.d.ts +3 -1
  88. package/dist/sdk/actions/index.js +3 -1
  89. package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
  90. package/dist/sdk/actions/locale-action-provider.js +20 -0
  91. package/dist/sdk/actions/locale-action-provider.js.map +1 -0
  92. package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
  93. package/dist/sdk/actions/saved-card-action-provider.js +16 -0
  94. package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
  95. package/dist/sdk/actions/types.d.ts +6 -2
  96. package/dist/sdk/cache/storefront-cache.js +2 -1
  97. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  98. package/dist/sdk/cart/operations.d.ts +2 -1
  99. package/dist/sdk/cart/operations.js +55 -1
  100. package/dist/sdk/cart/operations.js.map +1 -1
  101. package/dist/sdk/cart/optimistic.d.ts +30 -7
  102. package/dist/sdk/cart/optimistic.js +78 -41
  103. package/dist/sdk/cart/optimistic.js.map +1 -1
  104. package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
  105. package/dist/sdk/cart/use-cart-actions.js +61 -15
  106. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  107. package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
  108. package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
  109. package/dist/sdk/generated/gql.js +15 -11
  110. package/dist/sdk/generated/gql.js.map +1 -1
  111. package/dist/sdk/generated/graphql.d.ts +177 -46
  112. package/dist/sdk/generated/graphql.js +2314 -93
  113. package/dist/sdk/generated/graphql.js.map +1 -1
  114. package/dist/sdk/i18n/messages/en-US.js +19 -0
  115. package/dist/sdk/i18n/messages/en-US.json +19 -0
  116. package/dist/sdk/i18n/messages/en-US.json.d.ts +19 -0
  117. package/dist/sdk/i18n/messages/es-ES.js +19 -0
  118. package/dist/sdk/i18n/messages/es-ES.json +19 -0
  119. package/dist/sdk/i18n/messages/pt-BR.js +19 -0
  120. package/dist/sdk/i18n/messages/pt-BR.json +19 -0
  121. package/dist/sdk/i18n/provider.js +1 -1
  122. package/dist/sdk/i18n/provider.js.map +1 -1
  123. package/dist/sdk/locale/binding-switch-provider.d.ts +13 -0
  124. package/dist/sdk/locale/binding-switch-provider.js +20 -0
  125. package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
  126. package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
  127. package/dist/sdk/locale/use-locale-actions.js +70 -0
  128. package/dist/sdk/locale/use-locale-actions.js.map +1 -0
  129. package/dist/sdk/navigation/binding-switch.d.ts +39 -0
  130. package/dist/sdk/navigation/binding-switch.js +129 -0
  131. package/dist/sdk/navigation/binding-switch.js.map +1 -0
  132. package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
  133. package/dist/sdk/navigation/decode-route-params.js +32 -0
  134. package/dist/sdk/navigation/decode-route-params.js.map +1 -0
  135. package/dist/sdk/saved-cards/operations.d.ts +16 -0
  136. package/dist/sdk/saved-cards/operations.js +41 -0
  137. package/dist/sdk/saved-cards/operations.js.map +1 -0
  138. package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
  139. package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
  140. package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
  141. package/dist/sdk/seo/page-alternates.d.ts +5 -3
  142. package/dist/sdk/seo/page-alternates.js +7 -12
  143. package/dist/sdk/seo/page-alternates.js.map +1 -1
  144. package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
  145. package/dist/sdk/shipping/shipping-operations.js +13 -0
  146. package/dist/sdk/shipping/shipping-operations.js.map +1 -1
  147. package/dist/sdk/state/cart.d.ts +9 -2
  148. package/dist/sdk/state/cart.js +9 -2
  149. package/dist/sdk/state/cart.js.map +1 -1
  150. package/package.json +2 -2
  151. package/src/styles/tailwind.css +1 -2
@@ -0,0 +1,956 @@
1
+ "use client";
2
+ import { defineComponent } from "../../core/dist/define-component.js";
3
+ import { defineContentType } from "../../core/dist/define-content-type.js";
4
+ import "../../core/dist/index.js";
5
+ import { signInTarget } from "../../utils/safe-href.js";
6
+ import { useIntl } from "../../sdk/i18n/client.js";
7
+ import { useStorefrontRoute } from "../../sdk/navigation/use-storefront-route.js";
8
+ import { graphql } from "../../sdk/generated/gql.js";
9
+ import { ActionsOverrideContext, useActionsOverride } from "../../sdk/actions/actions-override-context.js";
10
+ import { useDataOverride } from "../../sdk/data/data-override-context.js";
11
+ import { resolveAuthoredCopy } from "../../utils/resolve-authored-copy.js";
12
+ import { useSession } from "../session/session.js";
13
+ import { NavigationLink } from "../../particles/navigation-link/navigation-link.js";
14
+ import { ListItemRefContext } from "../../sdk/data/list-item-ref-context.js";
15
+ import { ReactiveRootContext } from "../../sdk/data/page-reactive-data-root.js";
16
+ import { AlertDialogBackdrop, AlertDialogClose, AlertDialogDescription, AlertDialogPopup, AlertDialogPortal, AlertDialogRoot, AlertDialogTitle, AlertDialogTrigger, AlertDialogViewport } from "../../particles/alert-dialog/alert-dialog.js";
17
+ import { CardSummary, CardSummaryRoot, useCardSummary } from "../../particles/card-summary/card-summary.js";
18
+ import { cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
19
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
20
+ import { CombinedGraphQLErrors } from "@apollo/client/errors";
21
+ //#region src/matter/account-saved-cards/account-saved-cards.tsx
22
+ /**
23
+ * The signed-in shopper's saved cards in the account.
24
+ * The Root reads card ids only; each AccountSavedCardItem subscribes to its own CustomerSavedCard
25
+ * (ADR-0012) and provides `card-summary` and the row itself, so the schema arranges the CardSummary
26
+ * parts and AccountSavedCardExpiry in the row.
27
+ * Every text resolves children, then the CMS entry, then the `fs/saved-cards.*` catalog; the expiry
28
+ * line, repeated per row, takes no children.
29
+ * Removal state lives in the Root, because a successful removal unmounts the row it started from. The
30
+ * Root also declares the `savedCard.remove` write, so a remove control rendered by a default, which the
31
+ * compiler cannot wrap, still reaches the provider.
32
+ */
33
+ const fragment = graphql(`
34
+ fragment AccountSavedCardsData on Query {
35
+ customerSavedCards {
36
+ __typename
37
+ id
38
+ cards {
39
+ __typename
40
+ id
41
+ }
42
+ }
43
+ }
44
+ `);
45
+ const itemFragment = graphql(`
46
+ fragment AccountSavedCardItemData on Query {
47
+ customerSavedCards {
48
+ cards {
49
+ __typename
50
+ id
51
+ network {
52
+ name
53
+ }
54
+ lastDigits
55
+ expiry {
56
+ month
57
+ year
58
+ }
59
+ status
60
+ }
61
+ }
62
+ }
63
+ `);
64
+ const AccountSavedCardsHeadingContent = defineContentType({
65
+ id: "account-saved-cards-heading",
66
+ title: "Account Saved Cards Heading",
67
+ description: "Heading of the account's saved-cards section.",
68
+ schema: {
69
+ type: "object",
70
+ additionalProperties: false,
71
+ properties: { text: {
72
+ type: "string",
73
+ minLength: 1,
74
+ title: "Heading"
75
+ } }
76
+ }
77
+ });
78
+ const AccountSavedCardsEmptyContent = defineContentType({
79
+ id: "account-saved-cards-empty",
80
+ title: "Account Saved Cards Empty",
81
+ description: "Statement shown when the shopper has no saved cards.",
82
+ schema: {
83
+ type: "object",
84
+ additionalProperties: false,
85
+ properties: { text: {
86
+ type: "string",
87
+ minLength: 1,
88
+ title: "Empty statement"
89
+ } }
90
+ }
91
+ });
92
+ const AccountSavedCardsLoadingContent = defineContentType({
93
+ id: "account-saved-cards-loading",
94
+ title: "Account Saved Cards Loading",
95
+ description: "Announcement read to assistive technology while the saved cards load.",
96
+ schema: {
97
+ type: "object",
98
+ additionalProperties: false,
99
+ properties: { text: {
100
+ type: "string",
101
+ minLength: 1,
102
+ title: "Loading announcement"
103
+ } }
104
+ }
105
+ });
106
+ const AccountSavedCardsErrorContent = defineContentType({
107
+ id: "account-saved-cards-error",
108
+ title: "Account Saved Cards Error",
109
+ description: "Messages shown when the saved cards cannot be read.",
110
+ schema: {
111
+ type: "object",
112
+ additionalProperties: false,
113
+ properties: {
114
+ text: {
115
+ type: "string",
116
+ minLength: 1,
117
+ title: "Load failure message"
118
+ },
119
+ sessionExpiredText: {
120
+ type: "string",
121
+ minLength: 1,
122
+ title: "Session expired message"
123
+ },
124
+ signInLabel: {
125
+ type: "string",
126
+ minLength: 1,
127
+ title: "Sign-in link label"
128
+ }
129
+ }
130
+ }
131
+ });
132
+ const AccountSavedCardExpiryContent = defineContentType({
133
+ id: "account-saved-card-expiry",
134
+ title: "Account Saved Card Expiry",
135
+ description: "A saved card's expiry line. The date follows the label and is never authored.",
136
+ schema: {
137
+ type: "object",
138
+ additionalProperties: false,
139
+ properties: {
140
+ expiresLabel: {
141
+ type: "string",
142
+ minLength: 1,
143
+ title: "Label before a working card's date"
144
+ },
145
+ expiredLabel: {
146
+ type: "string",
147
+ minLength: 1,
148
+ title: "Label before an expired card's date"
149
+ },
150
+ expiredText: {
151
+ type: "string",
152
+ minLength: 1,
153
+ title: "Expired card with no date"
154
+ },
155
+ unusableText: {
156
+ type: "string",
157
+ minLength: 1,
158
+ title: "Unusable card"
159
+ }
160
+ }
161
+ }
162
+ });
163
+ const AccountSavedCardRemoveTriggerContent = defineContentType({
164
+ id: "account-saved-card-remove-trigger",
165
+ title: "Account Saved Card Remove Trigger",
166
+ description: "Label of the control that opens a saved card's removal prompt.",
167
+ schema: {
168
+ type: "object",
169
+ additionalProperties: false,
170
+ properties: { text: {
171
+ type: "string",
172
+ minLength: 1,
173
+ title: "Trigger label"
174
+ } }
175
+ }
176
+ });
177
+ const AccountSavedCardRemoveTitleContent = defineContentType({
178
+ id: "account-saved-card-remove-title",
179
+ title: "Account Saved Card Remove Title",
180
+ description: "Question that titles the removal prompt.",
181
+ schema: {
182
+ type: "object",
183
+ additionalProperties: false,
184
+ properties: { text: {
185
+ type: "string",
186
+ minLength: 1,
187
+ title: "Prompt title"
188
+ } }
189
+ }
190
+ });
191
+ const AccountSavedCardRemoveDescriptionContent = defineContentType({
192
+ id: "account-saved-card-remove-description",
193
+ title: "Account Saved Card Remove Description",
194
+ description: "What removing the card means, shown after the card in the removal prompt.",
195
+ schema: {
196
+ type: "object",
197
+ additionalProperties: false,
198
+ properties: { text: {
199
+ type: "string",
200
+ minLength: 1,
201
+ title: "Consequence"
202
+ } }
203
+ }
204
+ });
205
+ const AccountSavedCardRemoveCancelContent = defineContentType({
206
+ id: "account-saved-card-remove-cancel",
207
+ title: "Account Saved Card Remove Cancel",
208
+ description: "Label of the control that keeps the card.",
209
+ schema: {
210
+ type: "object",
211
+ additionalProperties: false,
212
+ properties: { text: {
213
+ type: "string",
214
+ minLength: 1,
215
+ title: "Cancel label"
216
+ } }
217
+ }
218
+ });
219
+ const AccountSavedCardRemoveConfirmContent = defineContentType({
220
+ id: "account-saved-card-remove-confirm",
221
+ title: "Account Saved Card Remove Confirm",
222
+ description: "Label of the control that removes the card, and its label while the removal runs.",
223
+ schema: {
224
+ type: "object",
225
+ additionalProperties: false,
226
+ properties: {
227
+ text: {
228
+ type: "string",
229
+ minLength: 1,
230
+ title: "Confirm label"
231
+ },
232
+ pendingText: {
233
+ type: "string",
234
+ minLength: 1,
235
+ title: "Label while removing"
236
+ }
237
+ }
238
+ }
239
+ });
240
+ const AccountSavedCardsRemoveFeedbackContent = defineContentType({
241
+ id: "account-saved-cards-remove-feedback",
242
+ title: "Account Saved Cards Remove Feedback",
243
+ description: "Messages that tell the shopper how a card removal ended.",
244
+ schema: {
245
+ type: "object",
246
+ additionalProperties: false,
247
+ properties: {
248
+ successText: {
249
+ type: "string",
250
+ minLength: 1,
251
+ title: "Card removed"
252
+ },
253
+ failureText: {
254
+ type: "string",
255
+ minLength: 1,
256
+ title: "Card not removed"
257
+ },
258
+ sessionExpiredText: {
259
+ type: "string",
260
+ minLength: 1,
261
+ title: "Session expired message"
262
+ },
263
+ signInLabel: {
264
+ type: "string",
265
+ minLength: 1,
266
+ title: "Sign-in link label"
267
+ }
268
+ }
269
+ }
270
+ });
271
+ const AccountSavedCardsContext = createContext(null);
272
+ /** The section state the nearest AccountSavedCardsRoot publishes. Throws outside one. */
273
+ function useAccountSavedCards() {
274
+ const ctx = useContext(AccountSavedCardsContext);
275
+ if (!ctx) throw new Error("AccountSavedCards parts must be used inside AccountSavedCardsRoot.");
276
+ return ctx;
277
+ }
278
+ const AccountSavedCardContext = createContext(null);
279
+ /** The card the nearest AccountSavedCardItem provides. Throws outside one. */
280
+ function useAccountSavedCard() {
281
+ const ctx = useContext(AccountSavedCardContext);
282
+ if (!ctx) throw new Error("AccountSavedCard parts must be used inside AccountSavedCardItem.");
283
+ return ctx;
284
+ }
285
+ const AccountSavedCardRemoveContext = createContext(null);
286
+ /** The remove controller the nearest AccountSavedCardRemove provides. Throws outside one. */
287
+ function useAccountSavedCardRemove() {
288
+ const ctx = useContext(AccountSavedCardRemoveContext);
289
+ if (!ctx) throw new Error("AccountSavedCardRemove parts must be used inside AccountSavedCardRemove.");
290
+ return ctx;
291
+ }
292
+ /** Only an explicit USABLE reads as usable, so a status this client does not know never looks usable. */
293
+ function savedCardState(status) {
294
+ if (status === "USABLE") return "usable";
295
+ if (status === "EXPIRED") return "expired";
296
+ return "unusable";
297
+ }
298
+ function validExpiry(expiry) {
299
+ if (!expiry) return null;
300
+ const { month, year } = expiry;
301
+ if (!Number.isInteger(month) || month < 1 || month > 12) return null;
302
+ if (!Number.isInteger(year) || year < 1e3 || year > 9999) return null;
303
+ return {
304
+ month,
305
+ year
306
+ };
307
+ }
308
+ const EMPTY_CARDS = Object.freeze([]);
309
+ /**
310
+ * The read's own error code. The root error is page-wide, so an error whose path names another field
311
+ * does not count; with no path at all, the first code stands.
312
+ */
313
+ function readErrorCode(error) {
314
+ if (!CombinedGraphQLErrors.is(error)) return null;
315
+ const code = (error.errors.find((entry) => entry.path?.[0] === "customerSavedCards") ?? error.errors.find((entry) => entry.path == null))?.extensions?.code;
316
+ return typeof code === "string" ? code : null;
317
+ }
318
+ /**
319
+ * One state for the whole section. A null slice plus an error is a failed read; an error beside a
320
+ * resolved slice belongs to another field of the page query and changes nothing here.
321
+ */
322
+ function resolveSectionState(data, loading, error) {
323
+ if (data == null) {
324
+ if (error) return {
325
+ state: "error",
326
+ cards: EMPTY_CARDS,
327
+ errorCode: readErrorCode(error)
328
+ };
329
+ return {
330
+ state: loading ? "loading" : "idle",
331
+ cards: EMPTY_CARDS,
332
+ errorCode: null
333
+ };
334
+ }
335
+ const cards = data.cards ?? EMPTY_CARDS;
336
+ return {
337
+ state: cards.length === 0 ? "empty" : "ready",
338
+ cards,
339
+ errorCode: null
340
+ };
341
+ }
342
+ const AccountSavedCardsRootDefinition = defineComponent({
343
+ name: "AccountSavedCardsRoot",
344
+ importPath: "@vtex/faststore/components",
345
+ component: AccountSavedCardsRoot,
346
+ fragment,
347
+ fetchStrategy: "reactive",
348
+ requiresContext: "session",
349
+ providesContext: "account-saved-cards",
350
+ actions: { savedCard: ["remove"] },
351
+ editor: {
352
+ label: "Account Saved Cards Root",
353
+ category: "identity"
354
+ },
355
+ schema: {},
356
+ guideline: "The signed-in shopper's saved cards, read with no cart. Renders nothing unless the session is loggedIn, and no chrome of its own: compose AccountSavedCardsHeading, AccountSavedCardsRemoveFeedback, AccountSavedCardsLoading, AccountSavedCardsError, AccountSavedCardsEmpty, and AccountSavedCardsList (with an AccountSavedCardItem inside) as children. Exactly one of Loading, Error, Empty, or List renders at a time; the state is on data-fs-account-saved-cards-state. It owns the removal state and supplies the savedCard.remove write to every AccountSavedCardRemove inside, placed or default, so the write sits here rather than on the control that dispatches it; with no remove control composed the write is never called. Must be inside a SessionRoot, normally the my-account layout's. See account-saved-cards.md."
357
+ });
358
+ const AccountSavedCardsHeadingDefinition = defineComponent({
359
+ name: "AccountSavedCardsHeading",
360
+ importPath: "@vtex/faststore/components",
361
+ component: AccountSavedCardsHeading,
362
+ content: AccountSavedCardsHeadingContent,
363
+ requiresContext: "account-saved-cards",
364
+ editor: {
365
+ label: "Account Saved Cards Heading",
366
+ category: "identity"
367
+ },
368
+ schema: { as: {
369
+ type: [
370
+ "h2",
371
+ "h3",
372
+ "h4",
373
+ "h5",
374
+ "h6"
375
+ ],
376
+ default: "h2",
377
+ label: "Heading level"
378
+ } },
379
+ guideline: "The section heading, rendered in every state. `as` sets the level from h2 to h6 (default h2); there is no h1, because the page title belongs to the page shell — pick the level that fits where the section is nested, or omit the heading. Text resolves children, then the CMS entry, then the catalog (fs/saved-cards.heading). Must be inside AccountSavedCardsRoot."
380
+ });
381
+ const AccountSavedCardsListDefinition = defineComponent({
382
+ name: "AccountSavedCardsList",
383
+ importPath: "@vtex/faststore/components",
384
+ component: AccountSavedCardsList,
385
+ requiresContext: "account-saved-cards",
386
+ providesContext: "account-saved-card",
387
+ editor: {
388
+ label: "Account Saved Cards List",
389
+ category: "identity"
390
+ },
391
+ schema: {},
392
+ guideline: "The list of saved cards, one row per card in the platform order, rendered only when the shopper has at least one. Place one AccountSavedCardItem inside as the row template. Must be inside AccountSavedCardsRoot."
393
+ });
394
+ const AccountSavedCardItemDefinition = defineComponent({
395
+ name: "AccountSavedCardItem",
396
+ importPath: "@vtex/faststore/components",
397
+ component: AccountSavedCardItem,
398
+ fragment: itemFragment,
399
+ fetchStrategy: "reactive-list-item",
400
+ requiresContext: "account-saved-card",
401
+ providesContext: ["card-summary", "account-saved-card-data"],
402
+ editor: {
403
+ label: "Account Saved Card Item",
404
+ category: "identity"
405
+ },
406
+ schema: {},
407
+ guideline: "One saved card as a list row. It provides the card to the CardSummary parts, AccountSavedCardExpiry, and AccountSavedCardRemove. With no children it renders CardSummary (mark, network name, mask, last four digits), AccountSavedCardExpiry, and AccountSavedCardRemove, whose text is then always the catalog wording; compose them as children to rearrange them or to reword them from the CMS, keep AccountSavedCardExpiry in any custom row, and leave AccountSavedCardRemove out only for a read-only list. An expired row carries data-fs-account-saved-card-expired, an unusable one data-fs-account-saved-card-unusable, and a row whose removal is pending data-fs-account-saved-card-removing, for a swatch to dim. Never shows more than the last four digits. Must be inside AccountSavedCardsList."
408
+ });
409
+ const AccountSavedCardExpiryDefinition = defineComponent({
410
+ name: "AccountSavedCardExpiry",
411
+ importPath: "@vtex/faststore/components",
412
+ component: AccountSavedCardExpiry,
413
+ content: AccountSavedCardExpiryContent,
414
+ requiresContext: "account-saved-card-data",
415
+ editor: {
416
+ label: "Account Saved Card Expiry",
417
+ category: "identity"
418
+ },
419
+ schema: {},
420
+ guideline: "When a saved card expires: \"Expires 05/28\" for a working card, \"Expired 05/24\" (or \"Expired\" with no date) for an expired one, and \"This card can't be used\", with no date, for an unusable one. Renders nothing for a working card with no date. The label comes from the CMS entry, then the catalog (fs/saved-cards.expires, .expired, .expired-no-date, .unusable); the component always places the MM/YY date after it, and only a placed element reads the CMS entry. The state is on data-fs-account-saved-card-expiry-status (usable, expired, unusable). Must be inside AccountSavedCardItem."
421
+ });
422
+ const AccountSavedCardsEmptyDefinition = defineComponent({
423
+ name: "AccountSavedCardsEmpty",
424
+ importPath: "@vtex/faststore/components",
425
+ component: AccountSavedCardsEmpty,
426
+ content: AccountSavedCardsEmptyContent,
427
+ requiresContext: "account-saved-cards",
428
+ editor: {
429
+ label: "Account Saved Cards Empty",
430
+ category: "identity"
431
+ },
432
+ schema: {},
433
+ guideline: "States that the shopper has no saved cards. Renders only once the read resolved with none — never while loading or after a failure. Text resolves children, then the CMS entry, then the catalog (fs/saved-cards.empty). Must be inside AccountSavedCardsRoot."
434
+ });
435
+ const AccountSavedCardsLoadingDefinition = defineComponent({
436
+ name: "AccountSavedCardsLoading",
437
+ importPath: "@vtex/faststore/components",
438
+ component: AccountSavedCardsLoading,
439
+ content: AccountSavedCardsLoadingContent,
440
+ requiresContext: "account-saved-cards",
441
+ editor: {
442
+ label: "Account Saved Cards Loading",
443
+ category: "identity"
444
+ },
445
+ schema: { skeletonCount: {
446
+ type: "number",
447
+ default: 2,
448
+ label: "Placeholder rows"
449
+ } },
450
+ guideline: "Placeholder rows while the saved cards load, plus one polite live region that announces the wait (CMS entry, then the catalog fs/saved-cards.loading). The rows are hidden from assistive technology; style them through data-fs-account-saved-cards-loading-row. Without this part the section shows no loading look. Must be inside AccountSavedCardsRoot."
451
+ });
452
+ const AccountSavedCardsErrorDefinition = defineComponent({
453
+ name: "AccountSavedCardsError",
454
+ importPath: "@vtex/faststore/components",
455
+ component: AccountSavedCardsError,
456
+ content: AccountSavedCardsErrorContent,
457
+ requiresContext: "account-saved-cards",
458
+ editor: {
459
+ label: "Account Saved Cards Error",
460
+ category: "identity"
461
+ },
462
+ schema: {},
463
+ guideline: "An alert shown when the saved cards cannot be read. An expired session says so and links to sign-in, returning the shopper to this page; any other failure says the cards could not be loaded. The wording comes from the CMS entry, then the catalog (fs/saved-cards.session-expired, .sign-in, .load-failed), never from the server; children replace it for every failure and drop the link. Must be inside AccountSavedCardsRoot."
464
+ });
465
+ const AccountSavedCardRemoveDefinition = defineComponent({
466
+ name: "AccountSavedCardRemove",
467
+ importPath: "@vtex/faststore/components",
468
+ component: AccountSavedCardRemove,
469
+ requiresContext: "account-saved-card-data",
470
+ providesContext: "account-saved-card-remove",
471
+ editor: {
472
+ label: "Account Saved Card Remove",
473
+ category: "identity"
474
+ },
475
+ schema: {},
476
+ guideline: "Lets the shopper remove the row's card after confirming. State-only controller: wrap one AlertDialogRoot and it becomes controlled, so never set open or onOpenChange on it. Compose AlertDialogRoot > (AccountSavedCardRemoveTrigger + AlertDialogPortal > (AlertDialogBackdrop + AlertDialogViewport > AlertDialogPopup > (AccountSavedCardRemoveTitle + AccountSavedCardRemoveDescription + AccountSavedCardRemoveCancel + AccountSavedCardRemoveConfirm))), Cancel before Confirm so the safe action takes focus. With no children it renders that prompt with catalog text; compose the parts to reword them from the CMS. The prompt stays open while the removal runs and closes when it fails; on success the row leaves the list. AccountSavedCardsRoot supplies the savedCard.remove write, and the outcome goes unreported unless the section composes AccountSavedCardsRemoveFeedback. Must be inside AccountSavedCardItem."
477
+ });
478
+ const AccountSavedCardRemoveTriggerDefinition = defineComponent({
479
+ name: "AccountSavedCardRemoveTrigger",
480
+ importPath: "@vtex/faststore/components",
481
+ component: AccountSavedCardRemoveTrigger,
482
+ content: AccountSavedCardRemoveTriggerContent,
483
+ requiresContext: "account-saved-card-remove",
484
+ editor: {
485
+ label: "Account Saved Card Remove Trigger",
486
+ category: "identity"
487
+ },
488
+ schema: {},
489
+ guideline: "Opens the removal prompt. Its accessible name is its text followed by the card, e.g. \"Remove Visa ending in 1234\"; the card part is visually hidden. Disabled while any removal in the section is pending. Text resolves children, then the CMS entry, then the catalog (fs/saved-cards.remove.trigger). Direct child of the AlertDialogRoot inside AccountSavedCardRemove."
490
+ });
491
+ const AccountSavedCardRemoveTitleDefinition = defineComponent({
492
+ name: "AccountSavedCardRemoveTitle",
493
+ importPath: "@vtex/faststore/components",
494
+ component: AccountSavedCardRemoveTitle,
495
+ content: AccountSavedCardRemoveTitleContent,
496
+ requiresContext: "account-saved-card-remove",
497
+ editor: {
498
+ label: "Account Saved Card Remove Title",
499
+ category: "identity"
500
+ },
501
+ schema: {},
502
+ guideline: "The removal prompt's question and its accessible name. Required inside AlertDialogPopup. Text resolves children, then the CMS entry, then the catalog (fs/saved-cards.remove.title). Must be inside AccountSavedCardRemove."
503
+ });
504
+ const AccountSavedCardRemoveDescriptionDefinition = defineComponent({
505
+ name: "AccountSavedCardRemoveDescription",
506
+ importPath: "@vtex/faststore/components",
507
+ component: AccountSavedCardRemoveDescription,
508
+ content: AccountSavedCardRemoveDescriptionContent,
509
+ requiresContext: "account-saved-card-remove",
510
+ editor: {
511
+ label: "Account Saved Card Remove Description",
512
+ category: "identity"
513
+ },
514
+ schema: {},
515
+ guideline: "Names the card and states the consequence; screen readers announce it with the prompt. With no children it renders CardSummary and then the text, which resolves the CMS entry, then the catalog (fs/saved-cards.remove.description); children replace both, so keep a CardSummary in them for the prompt to name the card. Inside AlertDialogPopup, after AccountSavedCardRemoveTitle. Must be inside AccountSavedCardRemove."
516
+ });
517
+ const AccountSavedCardRemoveCancelDefinition = defineComponent({
518
+ name: "AccountSavedCardRemoveCancel",
519
+ importPath: "@vtex/faststore/components",
520
+ component: AccountSavedCardRemoveCancel,
521
+ content: AccountSavedCardRemoveCancelContent,
522
+ requiresContext: "account-saved-card-remove",
523
+ editor: {
524
+ label: "Account Saved Card Remove Cancel",
525
+ category: "identity"
526
+ },
527
+ schema: {},
528
+ guideline: "Closes the removal prompt and keeps the card. Disabled while the removal runs. Place it before AccountSavedCardRemoveConfirm so it takes focus when the prompt opens. Text resolves children, then the CMS entry, then the catalog (fs/saved-cards.remove.cancel). Must be inside AccountSavedCardRemove."
529
+ });
530
+ const AccountSavedCardRemoveConfirmDefinition = defineComponent({
531
+ name: "AccountSavedCardRemoveConfirm",
532
+ importPath: "@vtex/faststore/components",
533
+ component: AccountSavedCardRemoveConfirm,
534
+ content: AccountSavedCardRemoveConfirmContent,
535
+ requiresContext: "account-saved-card-remove",
536
+ editor: {
537
+ label: "Account Saved Card Remove Confirm",
538
+ category: "identity"
539
+ },
540
+ schema: {},
541
+ guideline: "Removes the card. It dispatches savedCard.remove through the write AccountSavedCardsRoot supplies and declares none of its own. While the removal runs it is busy and shows its pending text; one removal runs at a time in the section. Text resolves children, then the CMS entry, then the catalog (fs/saved-cards.remove.confirm, .pending). Must be inside AccountSavedCardRemove."
542
+ });
543
+ const AccountSavedCardsRemoveFeedbackDefinition = defineComponent({
544
+ name: "AccountSavedCardsRemoveFeedback",
545
+ importPath: "@vtex/faststore/components",
546
+ component: AccountSavedCardsRemoveFeedback,
547
+ content: AccountSavedCardsRemoveFeedbackContent,
548
+ requiresContext: "account-saved-cards",
549
+ editor: {
550
+ label: "Account Saved Cards Remove Feedback",
551
+ category: "identity"
552
+ },
553
+ schema: {},
554
+ guideline: "Tells the shopper how a card removal ended: a status message that takes focus when the card was removed, or an alert when it was not, with a sign-in link back to this page when the session expired. Renders in every state of the section, so the message outlives the removed row and an emptied list; it stays until the next removal starts. Without this part a removal's outcome goes unreported. The wording comes from the CMS entry, then the catalog (fs/saved-cards.remove.success, .failed, fs/saved-cards.session-expired, .sign-in), never from the server; the outcome is on data-fs-account-saved-cards-remove-feedback-outcome. Must be inside AccountSavedCardsRoot."
555
+ });
556
+ /**
557
+ * One removal at a time: each success replaces the whole cached list, so two in flight could land out
558
+ * of order and restore a card the later one removed.
559
+ */
560
+ function useRemovalState() {
561
+ const [pendingId, setPendingId] = useState(null);
562
+ const [outcome, setOutcome] = useState(null);
563
+ const pendingRef = useRef(null);
564
+ const begin = useCallback((id) => {
565
+ if (pendingRef.current !== null) return false;
566
+ pendingRef.current = id;
567
+ setPendingId(id);
568
+ setOutcome(null);
569
+ return true;
570
+ }, []);
571
+ const settle = useCallback((id, userErrors) => {
572
+ if (pendingRef.current !== id) return;
573
+ pendingRef.current = null;
574
+ setPendingId(null);
575
+ const [first] = userErrors;
576
+ setOutcome(first ? {
577
+ kind: "failed",
578
+ code: first.code
579
+ } : { kind: "removed" });
580
+ }, []);
581
+ return useMemo(() => ({
582
+ pendingId,
583
+ outcome,
584
+ begin,
585
+ settle
586
+ }), [
587
+ pendingId,
588
+ outcome,
589
+ begin,
590
+ settle
591
+ ]);
592
+ }
593
+ function AccountSavedCardsRoot({ $data: $dataProp, $actions: $actionsProp, className, children, ...props }) {
594
+ const { state: sessionState } = useSession();
595
+ const data = useDataOverride($dataProp) ?? null;
596
+ const $actions = useActionsOverride($actionsProp);
597
+ const { loading, error } = useContext(ReactiveRootContext);
598
+ const section = useMemo(() => resolveSectionState(data, loading, error), [
599
+ data,
600
+ loading,
601
+ error
602
+ ]);
603
+ const removal = useRemovalState();
604
+ const value = useMemo(() => ({
605
+ ...section,
606
+ removal
607
+ }), [section, removal]);
608
+ if (sessionState !== "loggedIn") return null;
609
+ return /* @__PURE__ */ jsx(AccountSavedCardsContext, {
610
+ value,
611
+ children: /* @__PURE__ */ jsx(ActionsOverrideContext, {
612
+ value: $actions,
613
+ children: /* @__PURE__ */ jsx("div", {
614
+ ...props,
615
+ className,
616
+ "data-fs-account-saved-cards": "",
617
+ "data-fs-account-saved-cards-state": value.state,
618
+ children
619
+ })
620
+ })
621
+ });
622
+ }
623
+ function AccountSavedCardsHeading({ as: Tag = "h2", $content, className, children, ...props }) {
624
+ useAccountSavedCards();
625
+ const intl = useIntl();
626
+ return /* @__PURE__ */ jsx(Tag, {
627
+ ...props,
628
+ className,
629
+ "data-fs-account-saved-cards-heading": "",
630
+ children: children ?? resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.heading" }))
631
+ });
632
+ }
633
+ function AccountSavedCardsList({ className, children, ...props }) {
634
+ const { state, cards } = useAccountSavedCards();
635
+ if (state !== "ready") return null;
636
+ return /* @__PURE__ */ jsx("ul", {
637
+ ...props,
638
+ className,
639
+ "data-fs-account-saved-cards-list": "",
640
+ children: cards.map((card) => /* @__PURE__ */ jsx(ListItemRefContext, {
641
+ value: {
642
+ __typename: "CustomerSavedCard",
643
+ id: card.id,
644
+ fallbackData: void 0
645
+ },
646
+ children
647
+ }, card.id))
648
+ });
649
+ }
650
+ function AccountSavedCardItem({ $data: $dataProp, className, children, ...props }) {
651
+ const card = useDataOverride($dataProp) ?? null;
652
+ const pendingId = useContext(AccountSavedCardsContext)?.removal.pendingId ?? null;
653
+ if (!card) return null;
654
+ const state = savedCardState(card.status);
655
+ return /* @__PURE__ */ jsx(AccountSavedCardContext, {
656
+ value: card,
657
+ children: /* @__PURE__ */ jsx("li", {
658
+ ...props,
659
+ className,
660
+ "data-fs-account-saved-card": "",
661
+ ...state === "expired" ? { "data-fs-account-saved-card-expired": "" } : {},
662
+ ...state === "unusable" ? { "data-fs-account-saved-card-unusable": "" } : {},
663
+ ...pendingId === card.id ? { "data-fs-account-saved-card-removing": "" } : {},
664
+ children: /* @__PURE__ */ jsx(CardSummaryRoot, {
665
+ network: card.network.name,
666
+ lastDigits: card.lastDigits,
667
+ children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
668
+ /* @__PURE__ */ jsx(CardSummary, {}),
669
+ /* @__PURE__ */ jsx(AccountSavedCardExpiry, {}),
670
+ /* @__PURE__ */ jsx(AccountSavedCardRemove, {})
671
+ ] })
672
+ })
673
+ })
674
+ });
675
+ }
676
+ function AccountSavedCardExpiry({ $content, className, ...props }) {
677
+ const { status, expiry } = useAccountSavedCard();
678
+ const intl = useIntl();
679
+ const state = savedCardState(status);
680
+ const date = state === "unusable" ? null : validExpiry(expiry);
681
+ if (state === "usable" && !date) return null;
682
+ let label;
683
+ if (state === "unusable") label = resolveAuthoredCopy($content?.unusableText, intl.formatMessage({ id: "fs/saved-cards.unusable" }));
684
+ else if (!date) label = resolveAuthoredCopy($content?.expiredText, intl.formatMessage({ id: "fs/saved-cards.expired-no-date" }));
685
+ else if (state === "expired") label = resolveAuthoredCopy($content?.expiredLabel, intl.formatMessage({ id: "fs/saved-cards.expired" }));
686
+ else label = resolveAuthoredCopy($content?.expiresLabel, intl.formatMessage({ id: "fs/saved-cards.expires" }));
687
+ const month = date ? String(date.month).padStart(2, "0") : "";
688
+ return /* @__PURE__ */ jsxs("p", {
689
+ ...props,
690
+ className,
691
+ "data-fs-account-saved-card-expiry": "",
692
+ "data-fs-account-saved-card-expiry-status": state,
693
+ children: [/* @__PURE__ */ jsx("span", {
694
+ "data-fs-account-saved-card-expiry-label": "",
695
+ children: label
696
+ }), date ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
697
+ " ",
698
+ /* @__PURE__ */ jsx("time", {
699
+ "data-fs-account-saved-card-expiry-date": "",
700
+ dateTime: `${date.year}-${month}`,
701
+ "aria-hidden": "true",
702
+ children: `${month}/${String(date.year % 100).padStart(2, "0")}`
703
+ }),
704
+ " ",
705
+ /* @__PURE__ */ jsx("span", {
706
+ "data-fs-sr-only": "",
707
+ className: "sr-only",
708
+ children: intl.formatDate(Date.UTC(date.year, date.month - 1, 1), {
709
+ month: "long",
710
+ year: "numeric",
711
+ timeZone: "UTC"
712
+ })
713
+ })
714
+ ] }) : null]
715
+ });
716
+ }
717
+ function AccountSavedCardsEmpty({ $content, className, children, ...props }) {
718
+ const { state } = useAccountSavedCards();
719
+ const intl = useIntl();
720
+ if (state !== "empty") return null;
721
+ return /* @__PURE__ */ jsx("p", {
722
+ ...props,
723
+ className,
724
+ "data-fs-account-saved-cards-empty": "",
725
+ children: children ?? resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.empty" }))
726
+ });
727
+ }
728
+ const DEFAULT_SKELETON_COUNT = 2;
729
+ function AccountSavedCardsLoading({ skeletonCount = DEFAULT_SKELETON_COUNT, $content, className, ...props }) {
730
+ const { state } = useAccountSavedCards();
731
+ const intl = useIntl();
732
+ const loading = state === "loading";
733
+ const label = resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.loading" }));
734
+ const rows = Number.isFinite(skeletonCount) ? Math.max(1, Math.trunc(skeletonCount)) : DEFAULT_SKELETON_COUNT;
735
+ const [announcement, setAnnouncement] = useState("");
736
+ const pendingLabel = loading ? label : "";
737
+ useEffect(() => {
738
+ setAnnouncement(pendingLabel);
739
+ }, [pendingLabel]);
740
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [loading ? /* @__PURE__ */ jsx("div", {
741
+ ...props,
742
+ className,
743
+ "aria-hidden": "true",
744
+ "data-fs-account-saved-cards-loading": "",
745
+ children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ jsx("span", { "data-fs-account-saved-cards-loading-row": "" }, index))
746
+ }) : null, /* @__PURE__ */ jsx("output", {
747
+ "aria-live": "polite",
748
+ "data-fs-account-saved-cards-loading-announcement": "",
749
+ children: announcement
750
+ })] });
751
+ }
752
+ function AccountSavedCardsError({ $content, className, children, ...props }) {
753
+ const { state, errorCode } = useAccountSavedCards();
754
+ const intl = useIntl();
755
+ const { path } = useStorefrontRoute();
756
+ if (state !== "error") return null;
757
+ const sessionExpired = errorCode === "UNAUTHORIZED";
758
+ return /* @__PURE__ */ jsx("div", {
759
+ ...props,
760
+ role: "alert",
761
+ className,
762
+ "data-fs-account-saved-cards-error": "",
763
+ "data-fs-account-saved-cards-error-code": sessionExpired ? "unauthorized" : "failed",
764
+ children: children ?? (sessionExpired ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
765
+ resolveAuthoredCopy($content?.sessionExpiredText, intl.formatMessage({ id: "fs/saved-cards.session-expired" })),
766
+ " ",
767
+ /* @__PURE__ */ jsx(NavigationLink, {
768
+ href: signInTarget(path),
769
+ prefetch: false,
770
+ "data-fs-account-saved-cards-sign-in": "",
771
+ children: resolveAuthoredCopy($content?.signInLabel, intl.formatMessage({ id: "fs/saved-cards.sign-in" }))
772
+ })
773
+ ] }) : resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.load-failed" })))
774
+ });
775
+ }
776
+ function AccountSavedCardRemove({ children }) {
777
+ const { id } = useAccountSavedCard();
778
+ const { removal } = useAccountSavedCards();
779
+ const [open, setOpen] = useState(false);
780
+ const pending = removal.pendingId === id;
781
+ const blocked = removal.pendingId !== null;
782
+ const close = useCallback(() => setOpen(false), []);
783
+ const value = useMemo(() => ({
784
+ id,
785
+ pending,
786
+ blocked,
787
+ close
788
+ }), [
789
+ id,
790
+ pending,
791
+ blocked,
792
+ close
793
+ ]);
794
+ const prompt = children ?? /* @__PURE__ */ jsx(AccountSavedCardRemoveDefaultPrompt, {});
795
+ if (!isValidElement(prompt)) return /* @__PURE__ */ jsx(AccountSavedCardRemoveContext, {
796
+ value,
797
+ children: prompt
798
+ });
799
+ return /* @__PURE__ */ jsx(AccountSavedCardRemoveContext, {
800
+ value,
801
+ children: cloneElement(prompt, {
802
+ open,
803
+ onOpenChange: (next) => {
804
+ if (next ? blocked : pending) return;
805
+ setOpen(next);
806
+ }
807
+ })
808
+ });
809
+ }
810
+ function AccountSavedCardRemoveDefaultPrompt({ open, onOpenChange }) {
811
+ return /* @__PURE__ */ jsxs(AlertDialogRoot, {
812
+ open,
813
+ onOpenChange,
814
+ children: [/* @__PURE__ */ jsx(AccountSavedCardRemoveTrigger, {}), /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [/* @__PURE__ */ jsx(AlertDialogBackdrop, {}), /* @__PURE__ */ jsx(AlertDialogViewport, { children: /* @__PURE__ */ jsxs(AlertDialogPopup, { children: [
815
+ /* @__PURE__ */ jsx(AccountSavedCardRemoveTitle, {}),
816
+ /* @__PURE__ */ jsx(AccountSavedCardRemoveDescription, {}),
817
+ /* @__PURE__ */ jsx(AccountSavedCardRemoveCancel, {}),
818
+ /* @__PURE__ */ jsx(AccountSavedCardRemoveConfirm, {})
819
+ ] }) })] })]
820
+ });
821
+ }
822
+ function AccountSavedCardRemoveTrigger({ $content, className, children, disabled, ...props }) {
823
+ const { blocked } = useAccountSavedCardRemove();
824
+ const { name, lastFour } = useCardSummary();
825
+ const intl = useIntl();
826
+ const card = [name, lastFour ? intl.formatMessage({ id: "fs/card.ending-in" }, { lastDigits: lastFour }) : null].filter(Boolean).join(" ");
827
+ return /* @__PURE__ */ jsxs(AlertDialogTrigger, {
828
+ ...props,
829
+ className,
830
+ disabled: blocked || disabled,
831
+ "data-fs-account-saved-card-remove-trigger": "",
832
+ children: [children ?? resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.remove.trigger" })), card ? /* @__PURE__ */ jsxs(Fragment$1, { children: [" ", /* @__PURE__ */ jsx("span", {
833
+ "data-fs-sr-only": "",
834
+ className: "sr-only",
835
+ children: card
836
+ })] }) : null]
837
+ });
838
+ }
839
+ function AccountSavedCardRemoveTitle({ $content, className, children, ...props }) {
840
+ useAccountSavedCardRemove();
841
+ const intl = useIntl();
842
+ return /* @__PURE__ */ jsx(AlertDialogTitle, {
843
+ ...props,
844
+ className,
845
+ "data-fs-account-saved-card-remove-title": "",
846
+ children: children ?? resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.remove.title" }))
847
+ });
848
+ }
849
+ function AccountSavedCardRemoveDescription({ $content, className, children, ...props }) {
850
+ useAccountSavedCardRemove();
851
+ const intl = useIntl();
852
+ return /* @__PURE__ */ jsx(AlertDialogDescription, {
853
+ ...props,
854
+ className,
855
+ "data-fs-account-saved-card-remove-description": "",
856
+ children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
857
+ /* @__PURE__ */ jsx(CardSummary, {}),
858
+ " ",
859
+ resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.remove.description" }))
860
+ ] })
861
+ });
862
+ }
863
+ function AccountSavedCardRemoveCancel({ $content, className, children, disabled, ...props }) {
864
+ const { pending } = useAccountSavedCardRemove();
865
+ const intl = useIntl();
866
+ return /* @__PURE__ */ jsx(AlertDialogClose, {
867
+ ...props,
868
+ className,
869
+ disabled: pending || disabled,
870
+ "data-fs-account-saved-card-remove-cancel": "",
871
+ children: children ?? resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.remove.cancel" }))
872
+ });
873
+ }
874
+ function AccountSavedCardRemoveConfirm({ $content, className, children, onClick, ...props }) {
875
+ const { id, pending, close } = useAccountSavedCardRemove();
876
+ const { removal } = useAccountSavedCards();
877
+ const remove = useActionsOverride(void 0)?.savedCard?.remove;
878
+ const intl = useIntl();
879
+ async function handleClick(event) {
880
+ onClick?.(event);
881
+ if (event.defaultPrevented || !remove || !removal.begin(id)) return;
882
+ let userErrors;
883
+ try {
884
+ userErrors = await remove({ id });
885
+ } catch {
886
+ userErrors = [{ code: "NETWORK_ERROR" }];
887
+ }
888
+ removal.settle(id, userErrors);
889
+ close();
890
+ }
891
+ return /* @__PURE__ */ jsx("button", {
892
+ ...props,
893
+ type: "button",
894
+ className,
895
+ "aria-disabled": pending || void 0,
896
+ "aria-busy": pending || void 0,
897
+ "data-fs-account-saved-card-remove-confirm": "",
898
+ ...pending ? { "data-fs-account-saved-card-remove-confirm-pending": "" } : {},
899
+ onClick: handleClick,
900
+ children: pending ? resolveAuthoredCopy($content?.pendingText, intl.formatMessage({ id: "fs/saved-cards.remove.pending" })) : children ?? resolveAuthoredCopy($content?.text, intl.formatMessage({ id: "fs/saved-cards.remove.confirm" }))
901
+ });
902
+ }
903
+ function feedbackOutcome(outcome) {
904
+ if (!outcome) return void 0;
905
+ if (outcome.kind === "removed") return "removed";
906
+ return outcome.code === "UNAUTHORIZED" ? "unauthorized" : "failed";
907
+ }
908
+ function AccountSavedCardsRemoveFeedback({ $content, className, ...props }) {
909
+ const { removal: { outcome } } = useAccountSavedCards();
910
+ const intl = useIntl();
911
+ const { path } = useStorefrontRoute();
912
+ const statusRef = useRef(null);
913
+ const kind = feedbackOutcome(outcome);
914
+ const successText = resolveAuthoredCopy($content?.successText, intl.formatMessage({ id: "fs/saved-cards.remove.success" }));
915
+ const [announcement, setAnnouncement] = useState("");
916
+ useEffect(() => {
917
+ const removed = outcome?.kind === "removed";
918
+ setAnnouncement(removed ? successText : "");
919
+ if (removed) statusRef.current?.focus();
920
+ }, [outcome, successText]);
921
+ return /* @__PURE__ */ jsxs("div", {
922
+ ...props,
923
+ className,
924
+ "data-fs-account-saved-cards-remove-feedback": "",
925
+ "data-fs-account-saved-cards-remove-feedback-outcome": kind,
926
+ children: [
927
+ /* @__PURE__ */ jsx("output", {
928
+ ref: statusRef,
929
+ "aria-live": "polite",
930
+ tabIndex: -1,
931
+ children: announcement
932
+ }),
933
+ kind === "unauthorized" ? /* @__PURE__ */ jsxs("div", {
934
+ role: "alert",
935
+ children: [
936
+ resolveAuthoredCopy($content?.sessionExpiredText, intl.formatMessage({ id: "fs/saved-cards.session-expired" })),
937
+ " ",
938
+ /* @__PURE__ */ jsx(NavigationLink, {
939
+ href: signInTarget(path),
940
+ prefetch: false,
941
+ "data-fs-account-saved-cards-remove-feedback-sign-in": "",
942
+ children: resolveAuthoredCopy($content?.signInLabel, intl.formatMessage({ id: "fs/saved-cards.sign-in" }))
943
+ })
944
+ ]
945
+ }) : null,
946
+ kind === "failed" ? /* @__PURE__ */ jsx("div", {
947
+ role: "alert",
948
+ children: resolveAuthoredCopy($content?.failureText, intl.formatMessage({ id: "fs/saved-cards.remove.failed" }))
949
+ }) : null
950
+ ]
951
+ });
952
+ }
953
+ //#endregion
954
+ export { AccountSavedCardExpiry, AccountSavedCardExpiryContent, AccountSavedCardExpiryDefinition, AccountSavedCardItem, AccountSavedCardItemDefinition, AccountSavedCardRemove, AccountSavedCardRemoveCancel, AccountSavedCardRemoveCancelContent, AccountSavedCardRemoveCancelDefinition, AccountSavedCardRemoveConfirm, AccountSavedCardRemoveConfirmContent, AccountSavedCardRemoveConfirmDefinition, AccountSavedCardRemoveDefinition, AccountSavedCardRemoveDescription, AccountSavedCardRemoveDescriptionContent, AccountSavedCardRemoveDescriptionDefinition, AccountSavedCardRemoveTitle, AccountSavedCardRemoveTitleContent, AccountSavedCardRemoveTitleDefinition, AccountSavedCardRemoveTrigger, AccountSavedCardRemoveTriggerContent, AccountSavedCardRemoveTriggerDefinition, AccountSavedCardsEmpty, AccountSavedCardsEmptyContent, AccountSavedCardsEmptyDefinition, AccountSavedCardsError, AccountSavedCardsErrorContent, AccountSavedCardsErrorDefinition, AccountSavedCardsHeading, AccountSavedCardsHeadingContent, AccountSavedCardsHeadingDefinition, AccountSavedCardsList, AccountSavedCardsListDefinition, AccountSavedCardsLoading, AccountSavedCardsLoadingContent, AccountSavedCardsLoadingDefinition, AccountSavedCardsRemoveFeedback, AccountSavedCardsRemoveFeedbackContent, AccountSavedCardsRemoveFeedbackDefinition, AccountSavedCardsRoot, AccountSavedCardsRootDefinition, useAccountSavedCard, useAccountSavedCardRemove, useAccountSavedCards };
955
+
956
+ //# sourceMappingURL=account-saved-cards.js.map