@fluid-app/portal-sdk 0.1.450 → 0.1.451
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.
- package/dist/{FluidProvider-BjSf_FiB.cjs → FluidProvider-BULkBuyn.cjs} +121 -28
- package/dist/FluidProvider-BULkBuyn.cjs.map +1 -0
- package/dist/{FluidProvider-CXqSq1iX.mjs → FluidProvider-CA_HAl9a.mjs} +116 -29
- package/dist/FluidProvider-CA_HAl9a.mjs.map +1 -0
- package/dist/{MessagingScreen-Ck5kiA-O.mjs → MessagingScreen-BdsSr7l4.mjs} +2 -2
- package/dist/{MessagingScreen-NdUsPc_N.cjs → MessagingScreen-CApfcPd5.cjs} +2 -2
- package/dist/{MessagingScreen-NdUsPc_N.cjs.map → MessagingScreen-CApfcPd5.cjs.map} +1 -1
- package/dist/{MessagingScreen-CtMw_Wp3.mjs → MessagingScreen-Cf0OOWZ5.mjs} +2 -2
- package/dist/{MessagingScreen-CtMw_Wp3.mjs.map → MessagingScreen-Cf0OOWZ5.mjs.map} +1 -1
- package/dist/{MessagingScreen-BAl4Hg2w.cjs → MessagingScreen-N3CiZ807.cjs} +2 -2
- package/dist/{OrdersScreen-B3sEroyv.mjs → OrdersScreen-BPihQMaX.mjs} +2 -2
- package/dist/{OrdersScreen-B3sEroyv.mjs.map → OrdersScreen-BPihQMaX.mjs.map} +1 -1
- package/dist/{OrdersScreen-iCm9w3Kz.cjs → OrdersScreen-CgU8hdeD.cjs} +2 -2
- package/dist/{OrdersScreen-Dz_rz68P.mjs → OrdersScreen-Cv-qovZG.mjs} +2 -2
- package/dist/{OrdersScreen-CNCGv-jL.cjs → OrdersScreen-DEv_TOuP.cjs} +2 -2
- package/dist/{OrdersScreen-CNCGv-jL.cjs.map → OrdersScreen-DEv_TOuP.cjs.map} +1 -1
- package/dist/{ProfileScreen--xpXB--h.mjs → ProfileScreen-BPPBknUb.mjs} +3 -3
- package/dist/{ProfileScreen-Ce8jwoAg.cjs → ProfileScreen-Bp3kvBsM.cjs} +3 -3
- package/dist/{ProfileScreen-CCVbDYek.cjs → ProfileScreen-Dh1zfLSC.cjs} +3 -3
- package/dist/{ProfileScreen-CCVbDYek.cjs.map → ProfileScreen-Dh1zfLSC.cjs.map} +1 -1
- package/dist/{ProfileScreen-DD8HaCNH.mjs → ProfileScreen-Gvi6qWH9.mjs} +3 -3
- package/dist/{ProfileScreen-DD8HaCNH.mjs.map → ProfileScreen-Gvi6qWH9.mjs.map} +1 -1
- package/dist/{ShopScreen-DsKCSRzS.mjs → ShopScreen-BQu3Hv89.mjs} +2 -2
- package/dist/{ShopScreen-DsKCSRzS.mjs.map → ShopScreen-BQu3Hv89.mjs.map} +1 -1
- package/dist/{ShopScreen-Cm-B46dF.cjs → ShopScreen-DqdBdkjS.cjs} +2 -2
- package/dist/{ShopScreen-Cm-B46dF.cjs.map → ShopScreen-DqdBdkjS.cjs.map} +1 -1
- package/dist/{ShopScreen-B9xABYoy.mjs → ShopScreen-eC3clAjp.mjs} +2 -2
- package/dist/{ShopScreen-CRVZOMY5.cjs → ShopScreen-lyjY8yxm.cjs} +2 -2
- package/dist/{SubscriptionsScreen-D1qYFGmN.cjs → SubscriptionsScreen-CYCQcYMB.cjs} +3 -3
- package/dist/{SubscriptionsScreen-C17iAZ_N.mjs → SubscriptionsScreen-Db9gI6D6.mjs} +3 -3
- package/dist/{SubscriptionsScreen-DhxSxQ43.cjs → SubscriptionsScreen-aaF2B1qD.cjs} +3 -3
- package/dist/{SubscriptionsScreen-DhxSxQ43.cjs.map → SubscriptionsScreen-aaF2B1qD.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-IWNnfQ36.mjs → SubscriptionsScreen-bCXc9Bqn.mjs} +3 -3
- package/dist/{SubscriptionsScreen-IWNnfQ36.mjs.map → SubscriptionsScreen-bCXc9Bqn.mjs.map} +1 -1
- package/dist/index.cjs +34 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +34 -22
- package/dist/index.mjs.map +1 -1
- package/dist/{use-portal-card-add-3ds-flow-Ch7FwzUp.cjs → use-portal-card-add-3ds-flow-1JkstOR0.cjs} +2 -2
- package/dist/{use-portal-card-add-3ds-flow-Ch7FwzUp.cjs.map → use-portal-card-add-3ds-flow-1JkstOR0.cjs.map} +1 -1
- package/dist/{use-portal-card-add-3ds-flow-hgln6QRL.mjs → use-portal-card-add-3ds-flow-DueNr2yx.mjs} +2 -2
- package/dist/{use-portal-card-add-3ds-flow-hgln6QRL.mjs.map → use-portal-card-add-3ds-flow-DueNr2yx.mjs.map} +1 -1
- package/package.json +8 -8
- package/dist/FluidProvider-BjSf_FiB.cjs.map +0 -1
- package/dist/FluidProvider-CXqSq1iX.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-portal-card-add-3ds-flow-hgln6QRL.mjs","names":["FormTextField","FormTextField","FormTextField"],"sources":["../../../profile/core/src/translation-api-context.ts","../src/providers/ProfileTranslationBridge.tsx","../../../profile/ui/src/components/ellipses-dropdown.tsx","../../../profile/ui/src/components/confirm-action-dialog.tsx","../../../profile/ui/src/components/form-fields.tsx","../../../profile/ui/src/components/user-info-dialog.tsx","../../../profile/ui/src/components/payment-method-card-internals.tsx","../../../profile/core/src/context.ts","../../../fluid-pay/core/src/context.ts","../../../fluid-pay/core/src/provider.tsx","../../../fluid-pay/core/src/utils/country-config.ts","../../../fluid-pay/core/src/hooks/use-country-states.ts","../../../profile/ui/src/components/edit-payment-method-dialog.tsx","../../../profile/ui/src/components/my-site-profile-section.tsx","../../../fluid-pay/ui/src/components/form-fields/FormTextField.tsx","../../../fluid-pay/ui/src/components/form-fields/FormComboboxField.tsx","../../../fluid-pay/ui/src/components/AddressFormDialog.tsx","../../../fluid-pay/ui/src/hooks/use-vgs-collect.ts","../../../fluid-pay/ui/src/components/CreditCardFormDialog.tsx","../../../fluid-pay/ui/src/components/VgsThreeDSModal.tsx","../../../fluid-pay/ui/src/hooks/three-ds-attempt.ts","../../../fluid-pay/ui/src/hooks/use-vgs-3ds-completion.ts","../../../fluid-pay/core/src/utils/browser-info.ts","../../core/src/pay-types.ts","../src/adapters/fluid-pay-api-adapter.ts","../../../platform/address-autocomplete/src/administrative-area.ts","../../../platform/address-autocomplete/src/components/AddressAutocompleteInput.tsx","../../../fluid-pay/core/src/utils/fingerprinting.ts","../src/hooks/three-ds-card-add-helpers.ts","../src/hooks/use-portal-card-add-3ds-flow.tsx"],"sourcesContent":["import type { Provider } from \"react\";\nimport { createTranslationContext } from \"@fluid-app/i18n/translation-api-context-factory\";\nimport type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport type { ProfileDict } from \"./translation-dictionary\";\n\nconst { Provider: ProfileProvider, useTranslation } =\n createTranslationContext<ProfileDict>(\"Profile\");\n\nexport const ProfileTranslationProvider: Provider<TranslationApi<ProfileDict> | null> =\n ProfileProvider;\nexport const useProfileTranslation = useTranslation;\n","import { useMemo, type ReactNode } from \"react\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useDomainDict } from \"@fluid-app/i18n/use-domain-dict\";\nimport {\n profileDomain,\n createProfileTranslationAdapter,\n} from \"@fluid-app/profile-core/translation-adapter\";\nimport { ProfileTranslationProvider } from \"@fluid-app/profile-core/translation-api-context\";\n\nexport function ProfileTranslationBridge({\n children,\n}: {\n children: ReactNode;\n}): React.JSX.Element {\n const { locale } = useActiveLocale();\n const dict = useDomainDict(profileDomain, locale);\n const api = useMemo(\n () => createProfileTranslationAdapter(locale, dict),\n [locale, dict],\n );\n return (\n <ProfileTranslationProvider value={api}>\n {children}\n </ProfileTranslationProvider>\n );\n}\n","import type { JSX } from \"react\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\nexport default function EllipsesDropdown({\n onDelete,\n onEdit,\n onMakeDefault,\n editLabel,\n deleteLabel,\n makeDefaultLabel,\n disabled = false,\n}: {\n onDelete?: () => void;\n onEdit?: () => void;\n onMakeDefault?: () => void;\n editLabel?: string;\n deleteLabel?: string;\n makeDefaultLabel?: string;\n disabled?: boolean;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const resolvedEditLabel = editLabel ?? t(\"edit\");\n const resolvedDeleteLabel = deleteLabel ?? t(\"delete\");\n const resolvedMakeDefaultLabel = makeDefaultLabel ?? t(\"make_default\");\n if (disabled) {\n return (\n <Button variant=\"ghost\" disabled>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 128 512\"\n className=\"fill-muted-foreground h-4 w-1\"\n >\n <path d=\"M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z\" />\n </svg>\n </Button>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 128 512\"\n className=\"fill-muted-foreground h-4 w-1\"\n >\n <path d=\"M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z\" />\n </svg>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"text-foreground min-w-28 space-y-1 text-sm\">\n {onEdit && (\n <>\n <DropdownMenuItem\n className=\"hover:bg-muted flex cursor-pointer flex-row items-center gap-x-2 rounded px-2\"\n onClick={(e) => {\n e.stopPropagation();\n onEdit();\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-3.5 flex-shrink-0\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10\"\n />\n </svg>\n <span>{resolvedEditLabel}</span>\n </DropdownMenuItem>\n {(onMakeDefault || onDelete) && <DropdownMenuSeparator />}\n </>\n )}\n {onMakeDefault && (\n <>\n <DropdownMenuItem\n className=\"hover:bg-muted flex cursor-pointer flex-row items-center gap-x-2 rounded px-2\"\n onClick={(e) => {\n e.stopPropagation();\n onMakeDefault();\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-3.5 flex-shrink-0\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m4.5 12.75 6 6 9-13.5\"\n />\n </svg>\n <span>{resolvedMakeDefaultLabel}</span>\n </DropdownMenuItem>\n {onDelete && <DropdownMenuSeparator />}\n </>\n )}\n {onDelete && (\n <DropdownMenuItem\n className=\"text-destructive hover:bg-destructive/10 flex cursor-pointer flex-row items-center gap-x-2 rounded px-2\"\n onClick={(e) => {\n e.stopPropagation();\n onDelete();\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-3.5 flex-shrink-0\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0\"\n />\n </svg>\n <span>{resolvedDeleteLabel}</span>\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","import type { JSX } from \"react\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n DialogClose,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\ntype ConfirmActionDialogProps = {\n title: string;\n description: string;\n onAction: () => void;\n openDialog: boolean;\n setOpenDialog: (open: boolean) => void;\n errorMsg?: string;\n isLoading?: boolean;\n actionText?: string;\n};\n\nexport default function ConfirmActionDialog({\n title,\n description,\n onAction,\n openDialog,\n setOpenDialog,\n errorMsg,\n isLoading,\n actionText,\n}: ConfirmActionDialogProps): JSX.Element {\n const { t } = useProfileTranslation();\n const resolvedActionText = actionText ?? t(\"delete\");\n\n return (\n <Dialog open={openDialog} onOpenChange={setOpenDialog}>\n <DialogContent className=\"max-w-sm md:w-90\">\n <DialogHeader className=\"flex flex-row justify-between\">\n <DialogTitle className=\"w-full text-left\">{title}</DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <p className=\"text-muted-foreground text-left text-sm\">\n {description}\n </p>\n </div>\n\n {errorMsg && <p className=\"text-destructive text-sm\">{errorMsg}</p>}\n\n <DialogFooter>\n <div className=\"flex w-full flex-row justify-between gap-x-2\">\n <DialogClose asChild>\n <Button\n variant=\"secondary\"\n className=\"min-w-[70px]\"\n onClick={() => setOpenDialog(false)}\n >\n {t(\"cancel\")}\n </Button>\n </DialogClose>\n <Button\n type=\"button\"\n variant=\"destructive\"\n className=\"min-w-[70px]\"\n onClick={onAction}\n disabled={isLoading}\n >\n {isLoading ? (\n <div className=\"border-primary-foreground size-5 animate-spin rounded-full border-4 border-t-4 border-t-transparent\" />\n ) : (\n resolvedActionText\n )}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import type React from \"react\";\nimport {\n useController,\n type Control,\n type FieldPath,\n type FieldValues,\n} from \"react-hook-form\";\nimport {\n Input,\n Label,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n Textarea,\n cn,\n} from \"@fluid-app/ui-primitives\";\n\nexport type NameValue = { name: string; value: string | number };\n\nexport function FormTextField<T extends FieldValues>({\n control,\n name,\n label,\n containerClassName,\n ...props\n}: {\n control: Control<T>;\n name: FieldPath<T>;\n label?: string;\n containerClassName?: string;\n} & Omit<React.ComponentProps<\"input\">, \"name\">): React.JSX.Element {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Input\n {...field}\n {...props}\n id={name}\n value={field.value ?? \"\"}\n className={cn(error && \"ring-destructive ring-1\", props.className)}\n />\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n\nexport function FormTextareaField<T extends FieldValues>({\n control,\n name,\n label,\n containerClassName,\n ...props\n}: {\n control: Control<T>;\n name: FieldPath<T>;\n label?: string;\n containerClassName?: string;\n} & Omit<React.ComponentProps<\"textarea\">, \"name\">): React.JSX.Element {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Textarea\n {...field}\n {...props}\n id={name}\n value={field.value ?? \"\"}\n className={cn(error && \"ring-destructive ring-1\", props.className)}\n />\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n\nexport function FormSelectField<T extends FieldValues>({\n control,\n name,\n label,\n options,\n placeholder,\n containerClassName,\n onChange,\n}: {\n control: Control<T>;\n name: FieldPath<T>;\n label?: string;\n options?: NameValue[];\n placeholder?: string;\n containerClassName?: string;\n onChange?: (value: string) => void;\n}): React.JSX.Element {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Select\n value={field.value?.toString() ?? \"\"}\n onValueChange={(val) => {\n field.onChange(val);\n onChange?.(val);\n }}\n >\n <SelectTrigger\n id={name}\n className={cn(\"w-full\", error && \"ring-destructive ring-1\")}\n >\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options?.map((opt) => (\n <SelectItem\n key={`${opt.name}-${opt.value}`}\n value={opt.value.toString()}\n >\n {opt.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n","import { useState, type JSX } from \"react\";\nimport type { Control } from \"react-hook-form\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n Button,\n Input,\n} from \"@fluid-app/ui-primitives\";\nimport { Info } from \"lucide-react\";\nimport {\n FormTextField,\n FormTextareaField,\n FormSelectField,\n type NameValue,\n} from \"./form-fields\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\nexport type UserFormData = {\n first_name: string;\n last_name: string;\n phone_number?: string;\n language: string;\n bio?: string;\n};\n\nexport default function UserInfoDialog({\n control,\n isOpen,\n onSubmit,\n handleClose,\n languageOptions,\n errorMsg,\n isSubmitting,\n email,\n onChangeEmail,\n isChangingEmail,\n pendingEmail,\n}: {\n control: Control<UserFormData>;\n isOpen: boolean;\n onSubmit: () => void;\n handleClose: () => void;\n languageOptions?: NameValue[];\n errorMsg: string | undefined;\n isSubmitting: boolean;\n email?: string;\n onChangeEmail?: (newEmail: string) => Promise<void> | void;\n isChangingEmail?: boolean;\n pendingEmail?: string;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const [isEditingEmail, setIsEditingEmail] = useState(false);\n const [newEmail, setNewEmail] = useState(\"\");\n const [emailError, setEmailError] = useState<string | undefined>(undefined);\n\n const handleEmailSubmit = async () => {\n setEmailError(undefined);\n const trimmed = newEmail.trim();\n if (!trimmed) {\n setEmailError(t(\"please_enter_new_email\"));\n return;\n }\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(trimmed)) {\n setEmailError(t(\"please_enter_valid_email\"));\n return;\n }\n if (email && trimmed.toLowerCase() === email.toLowerCase()) {\n setEmailError(t(\"new_email_must_be_different\"));\n return;\n }\n try {\n await onChangeEmail?.(trimmed);\n setIsEditingEmail(false);\n setNewEmail(\"\");\n } catch (err) {\n setEmailError(\n err instanceof Error\n ? err.message\n : t(\"failed_to_initiate_email_change\"),\n );\n }\n };\n\n const handleCancelEmailEdit = () => {\n setIsEditingEmail(false);\n setNewEmail(\"\");\n setEmailError(undefined);\n };\n\n const handleDialogClose = () => {\n handleCancelEmailEdit();\n handleClose();\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && handleDialogClose()}>\n <DialogContent className=\"max-w-sm md:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t(\"edit_profile\")}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <FormTextField\n control={control}\n name=\"first_name\"\n label={t(\"first_name\")}\n />\n <FormTextField\n control={control}\n name=\"last_name\"\n label={t(\"last_name\")}\n />\n <FormTextField\n control={control}\n name=\"phone_number\"\n label={t(\"phone_number\")}\n type=\"tel\"\n />\n <FormTextareaField\n control={control}\n name=\"bio\"\n label={t(\"bio\")}\n rows={3}\n placeholder={t(\"add_a_short_bio\")}\n />\n <FormSelectField\n control={control}\n name=\"language\"\n label={t(\"language\")}\n options={languageOptions}\n placeholder={t(\"select_an_option\")}\n />\n </div>\n {email && (\n <div className=\"space-y-2\">\n <label className=\"text-foreground text-sm font-medium\">\n {t(\"email\")}\n </label>\n {isEditingEmail ? (\n <div className=\"space-y-2\">\n <Input\n type=\"email\"\n placeholder={t(\"enter_new_email_placeholder\")}\n value={newEmail}\n onChange={(e) => {\n setNewEmail(e.target.value);\n setEmailError(undefined);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleEmailSubmit();\n }\n if (e.key === \"Escape\") {\n handleCancelEmailEdit();\n }\n }}\n aria-invalid={!!emailError || undefined}\n autoFocus\n />\n {emailError && (\n <p className=\"text-destructive text-xs\">{emailError}</p>\n )}\n <div className=\"bg-muted flex items-start gap-2.5 rounded-md px-3 py-2.5\">\n <Info className=\"text-primary mt-0.5 size-4 shrink-0\" />\n <p className=\"text-muted-foreground text-xs\">\n {t(\"email_verification_hint\")}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCancelEmailEdit}\n >\n {t(\"cancel\")}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={handleEmailSubmit}\n disabled={!newEmail.trim() || isChangingEmail}\n >\n {isChangingEmail && (\n <div className=\"mr-2 size-3.5 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isChangingEmail ? t(\"sending\") : t(\"send_verification\")}\n </Button>\n </div>\n </div>\n ) : pendingEmail ? (\n <div className=\"space-y-2\">\n <div className=\"bg-muted text-muted-foreground min-w-0 rounded-md px-3 py-2 text-sm\">\n {email}\n </div>\n <div className=\"bg-muted flex items-start gap-2.5 rounded-md px-3 py-2.5\">\n <Info className=\"text-primary mt-0.5 size-4 shrink-0\" />\n <p className=\"text-muted-foreground text-xs\">\n {t(\"changing_email_to\")}{\" \"}\n <span className=\"text-foreground font-medium\">\n {pendingEmail}\n </span>\n . {t(\"check_new_email_verification\")}\n </p>\n </div>\n </div>\n ) : (\n <div className=\"flex items-center gap-2\">\n <div className=\"bg-muted text-muted-foreground min-w-0 flex-1 rounded-md px-3 py-2 text-sm\">\n {email}\n </div>\n {onChangeEmail && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setIsEditingEmail(true)}\n className=\"shrink-0\"\n >\n {t(\"change\")}\n </Button>\n )}\n </div>\n )}\n </div>\n )}\n {errorMsg && <p className=\"text-destructive text-sm\">{errorMsg}</p>}\n <DialogFooter className=\"flex flex-row items-center justify-end\">\n <div className=\"flex-1 text-right\">\n <Button type=\"button\" onClick={onSubmit}>\n {isSubmitting && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isSubmitting ? t(\"saving\") : t(\"save_changes\")}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import { useState, type JSX } from \"react\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport {\n brandIconUrl as resolveBrandIconUrl,\n normalizeCardBrand,\n} from \"@fluid-app/payments-display/brands\";\nimport {\n isCardPaymentDescriptor,\n normalizePaymentDescriptor,\n paymentDescriptorLabel,\n resolvePaymentLogo,\n resolvePaymentMethodDisplay,\n} from \"@fluid-app/payments-display/display\";\n\nexport function brandIconUrl(brand: string | null | undefined): string {\n return resolveBrandIconUrl(brand);\n}\n\nexport function PaymentIcon({\n logoUrl,\n brand,\n alt,\n}: {\n logoUrl?: string;\n brand: string | null | undefined;\n alt: string;\n}): JSX.Element {\n const logo = resolvePaymentLogo({ brand, logoUrl, alt });\n const [hasError, setHasError] = useState(false);\n const src = hasError ? logo.fallbackUrl : logo.primaryUrl;\n return (\n <img\n src={src}\n alt={logo.alt}\n width={40}\n height={24}\n className=\"object-contain\"\n onError={() => setHasError(true)}\n />\n );\n}\n\nexport function getPaymentMethodLogoBrand(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n): string | null {\n if (isCardPaymentMethod(paymentMethod)) {\n return (\n normalizeCardBrand(paymentMethod.details.card_brand) ??\n normalizeCardBrand(paymentMethod.details.card_network)\n );\n }\n\n return paymentDescriptorLabel(\n paymentMethod.payment_type,\n paymentMethod.source,\n );\n}\n\nexport function getCardDisplayName(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n cardFallback = \"Card\",\n): string {\n if (isCardPaymentMethod(paymentMethod)) {\n return resolvePaymentMethodDisplay({\n kind: \"card\",\n brand: getPaymentMethodLogoBrand(paymentMethod) ?? cardFallback,\n last4: paymentMethod.details.last_four,\n logoUrl: paymentMethod.details.logo_url,\n }).label;\n }\n\n const fallbackLabel = paymentDescriptorLabel(\n paymentMethod.payment_type,\n paymentMethod.source,\n );\n return resolvePaymentMethodDisplay({\n kind: \"non_card\",\n brand: fallbackLabel,\n fallbackLabel,\n logoUrl: paymentMethod.details.logo_url,\n }).label;\n}\n\nexport function getCardExpiry(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n expiresLabel = \"Expires\",\n): string {\n const { exp_month, exp_year } = paymentMethod.details;\n if (exp_month != null && exp_year != null) {\n return `${expiresLabel} ${exp_month}/${exp_year}`;\n }\n return \"\";\n}\n\nfunction isCardPaymentMethod(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n): boolean {\n return (\n paymentMethod.source === \"card\" ||\n isCardPaymentDescriptor(paymentMethod.payment_type) ||\n isCardPaymentDescriptor(paymentMethod.source) ||\n normalizePaymentDescriptor(paymentMethod.details.card_brand) != null ||\n normalizePaymentDescriptor(paymentMethod.details.card_network) != null ||\n paymentMethod.details.last_four != null\n );\n}\n","import { createContext, useContext } from \"react\";\n\nexport type TranslationFn = (key: string) => string;\n\nexport interface ProfileUIContextValue {\n t: TranslationFn;\n}\n\nexport const ProfileUIContext = createContext<ProfileUIContextValue | null>(\n null,\n);\n\nexport function useProfileUI(): ProfileUIContextValue {\n const context = useContext(ProfileUIContext);\n if (!context) {\n throw new Error(\"useProfileUI must be used within a ProfileUIProvider\");\n }\n return context;\n}\n","import { createContext, useContext } from \"react\";\nimport type { FluidPayApi } from \"./api-interface\";\n\nconst FluidPayApiContext = createContext<FluidPayApi | null>(null);\n\nexport const FluidPayApiProvider = FluidPayApiContext.Provider;\n\nexport function useFluidPayApi(): FluidPayApi {\n const api = useContext(FluidPayApiContext);\n if (!api) {\n throw new Error(\n \"useFluidPayApi must be used within a FluidPayCoreProvider\",\n );\n }\n return api;\n}\n","import type { JSX } from \"react\";\nimport type { FluidPayApi } from \"./api-interface\";\nimport { FluidPayApiProvider } from \"./context\";\n\nexport interface FluidPayCoreProviderProps {\n api: FluidPayApi;\n children: React.ReactNode;\n}\n\nexport function FluidPayCoreProvider({\n api,\n children,\n}: FluidPayCoreProviderProps): JSX.Element {\n return <FluidPayApiProvider value={api}>{children}</FluidPayApiProvider>;\n}\n","export interface State {\n name: string;\n isoCode: string;\n}\n\nexport interface RegionConfig {\n regionLabel: string;\n postalLabel: string;\n}\n\nexport const US_STATES: State[] = [\n { name: \"Alabama\", isoCode: \"AL\" },\n { name: \"Alaska\", isoCode: \"AK\" },\n { name: \"Arizona\", isoCode: \"AZ\" },\n { name: \"Arkansas\", isoCode: \"AR\" },\n { name: \"California\", isoCode: \"CA\" },\n { name: \"Colorado\", isoCode: \"CO\" },\n { name: \"Connecticut\", isoCode: \"CT\" },\n { name: \"Delaware\", isoCode: \"DE\" },\n { name: \"Florida\", isoCode: \"FL\" },\n { name: \"Georgia\", isoCode: \"GA\" },\n { name: \"Hawaii\", isoCode: \"HI\" },\n { name: \"Idaho\", isoCode: \"ID\" },\n { name: \"Illinois\", isoCode: \"IL\" },\n { name: \"Indiana\", isoCode: \"IN\" },\n { name: \"Iowa\", isoCode: \"IA\" },\n { name: \"Kansas\", isoCode: \"KS\" },\n { name: \"Kentucky\", isoCode: \"KY\" },\n { name: \"Louisiana\", isoCode: \"LA\" },\n { name: \"Maine\", isoCode: \"ME\" },\n { name: \"Maryland\", isoCode: \"MD\" },\n { name: \"Massachusetts\", isoCode: \"MA\" },\n { name: \"Michigan\", isoCode: \"MI\" },\n { name: \"Minnesota\", isoCode: \"MN\" },\n { name: \"Mississippi\", isoCode: \"MS\" },\n { name: \"Missouri\", isoCode: \"MO\" },\n { name: \"Montana\", isoCode: \"MT\" },\n { name: \"Nebraska\", isoCode: \"NE\" },\n { name: \"Nevada\", isoCode: \"NV\" },\n { name: \"New Hampshire\", isoCode: \"NH\" },\n { name: \"New Jersey\", isoCode: \"NJ\" },\n { name: \"New Mexico\", isoCode: \"NM\" },\n { name: \"New York\", isoCode: \"NY\" },\n { name: \"North Carolina\", isoCode: \"NC\" },\n { name: \"North Dakota\", isoCode: \"ND\" },\n { name: \"Ohio\", isoCode: \"OH\" },\n { name: \"Oklahoma\", isoCode: \"OK\" },\n { name: \"Oregon\", isoCode: \"OR\" },\n { name: \"Pennsylvania\", isoCode: \"PA\" },\n { name: \"Rhode Island\", isoCode: \"RI\" },\n { name: \"South Carolina\", isoCode: \"SC\" },\n { name: \"South Dakota\", isoCode: \"SD\" },\n { name: \"Tennessee\", isoCode: \"TN\" },\n { name: \"Texas\", isoCode: \"TX\" },\n { name: \"Utah\", isoCode: \"UT\" },\n { name: \"Vermont\", isoCode: \"VT\" },\n { name: \"Virginia\", isoCode: \"VA\" },\n { name: \"Washington\", isoCode: \"WA\" },\n { name: \"West Virginia\", isoCode: \"WV\" },\n { name: \"Wisconsin\", isoCode: \"WI\" },\n { name: \"Wyoming\", isoCode: \"WY\" },\n { name: \"District of Columbia\", isoCode: \"DC\" },\n];\n\nexport const COUNTRY_CONFIGS: Record<string, RegionConfig> = {\n US: { regionLabel: \"State\", postalLabel: \"ZIP Code\" },\n JP: { regionLabel: \"Prefecture\", postalLabel: \"Postal Code\" },\n};\n\nconst DEFAULT_REGION_CONFIG: RegionConfig = {\n regionLabel: \"Province\",\n postalLabel: \"Postal Code\",\n};\n\nexport function getRegionConfig(countryCode: string): RegionConfig {\n return COUNTRY_CONFIGS[countryCode] ?? DEFAULT_REGION_CONFIG;\n}\n\nexport const DEFAULT_ADDRESS_FIELDS = [\n { field: \"name\", label: \"Name\", required: true },\n { field: \"address1\", label: \"Address\", required: true },\n { field: \"address2\", label: \"Apartment, suite, etc.\", required: false },\n { field: \"city\", label: \"City\", required: true },\n { field: \"state\", label: \"State\", required: true },\n { field: \"postal_code\", label: \"Postal Code\", required: true },\n { field: \"country_code\", label: \"Country\", required: true },\n];\n","import { useCallback, useState } from \"react\";\nimport {\n US_STATES,\n getRegionConfig,\n type State,\n} from \"../utils/country-config\";\n\nexport default function useCountryStates() {\n const [selectedCountry, setSelectedCountry] = useState<string>(\"US\");\n const [stateOptions, setStateOptions] = useState<State[]>(US_STATES);\n\n const handleCountryChange = useCallback(\n (\n selectedCountryCode: string,\n fetchStates?: (countryCode: string) => Promise<State[]>,\n ) => {\n setSelectedCountry(selectedCountryCode);\n const fallbackStates = selectedCountryCode === \"US\" ? US_STATES : [];\n if (fetchStates) {\n fetchStates(selectedCountryCode)\n .then((states) =>\n setStateOptions(states.length > 0 ? states : fallbackStates),\n )\n .catch((error) => {\n console.error(\"Failed to fetch states:\", error);\n setStateOptions(fallbackStates);\n });\n } else if (selectedCountryCode === \"US\") {\n setStateOptions(US_STATES);\n } else {\n setStateOptions([]);\n }\n },\n [],\n );\n\n const config = getRegionConfig(selectedCountry);\n\n return { handleCountryChange, stateOptions, config, selectedCountry };\n}\n","import { useEffect, useMemo, useState, type JSX } from \"react\";\nimport { useWatch } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { Trash2 } from \"lucide-react\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n Button,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport { FormTextField, FormSelectField } from \"./form-fields\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport { useCountryStates } from \"@fluid-app/profile-core\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\nimport type { BillingAddress } from \"./view-payment-method-dialog\";\nimport {\n PaymentIcon,\n getCardDisplayName,\n getPaymentMethodLogoBrand,\n} from \"./payment-method-card-internals\";\n\nfunction createEditPaymentMethodFormSchema(t: (key: string) => string) {\n return z.object({\n billing_address: z.object({\n name: z.string().min(1, t(\"name_is_required\")),\n address1: z.string().min(1, t(\"address_line_1_is_required\")),\n address2: z.string().optional().nullable(),\n city: z.string().min(1, t(\"city_is_required\")),\n state: z.string().min(1, t(\"state_is_required\")),\n zip: z.string().min(1, t(\"zip_code_is_required\")),\n country_code: z.string().min(1, t(\"country_is_required\")),\n }),\n set_as_default: z.boolean(),\n });\n}\n\nexport type EditPaymentMethodFormData = z.infer<\n ReturnType<typeof createEditPaymentMethodFormSchema>\n>;\n\ninterface CountryOption {\n iso: string;\n name: string;\n}\n\ninterface EditPaymentMethodDialogProps {\n isOpen: boolean;\n paymentMethod: fluidPay.CustomerPaymentMethod | null;\n billingAddress?: BillingAddress | null;\n onClose: () => void;\n onSubmit: (data: EditPaymentMethodFormData) => void;\n isSubmitting: boolean;\n error?: string;\n countries?: CountryOption[];\n onDelete?: () => void;\n isDeleting?: boolean;\n}\n\nexport default function EditPaymentMethodDialog({\n isOpen,\n paymentMethod,\n billingAddress,\n onClose,\n onSubmit,\n isSubmitting,\n error,\n countries = [],\n onDelete,\n isDeleting = false,\n}: EditPaymentMethodDialogProps): JSX.Element | null {\n const { t } = useProfileTranslation();\n const [confirmDelete, setConfirmDelete] = useState(false);\n\n useEffect(() => {\n if (!isOpen) setConfirmDelete(false);\n }, [isOpen]);\n\n useEffect(() => {\n if (!confirmDelete) return;\n const timeoutId = setTimeout(() => setConfirmDelete(false), 3000);\n return () => clearTimeout(timeoutId);\n }, [confirmDelete]);\n\n const editPaymentMethodFormSchema = useMemo(\n () => createEditPaymentMethodFormSchema((key) => t(key as never)),\n [t],\n );\n\n const { control, handleSubmit, reset, setValue } =\n useZodForm<EditPaymentMethodFormData>(editPaymentMethodFormSchema, {\n defaultValues: {\n billing_address: {\n name: \"\",\n address1: \"\",\n address2: \"\",\n city: \"\",\n state: \"\",\n zip: \"\",\n country_code: \"US\",\n },\n set_as_default: false,\n },\n });\n\n const billingCountry = useWatch({\n control,\n name: \"billing_address.country_code\",\n });\n const setAsDefault = useWatch({ control, name: \"set_as_default\" });\n\n const { stateOptions, config, handleCountryChange } = useCountryStates();\n\n const countryOptions = [...countries]\n .map((country) => ({\n name: country.name,\n value: country.iso,\n }))\n .sort((a, b) => {\n if (a.value === billingCountry) return -1;\n if (b.value === billingCountry) return 1;\n return a.name.localeCompare(b.name);\n });\n\n const stateSelectOptions = stateOptions.map((state) => ({\n name: state.name,\n value: state.isoCode,\n }));\n\n useEffect(() => {\n if (paymentMethod && isOpen) {\n reset({\n billing_address: {\n name: billingAddress?.name ?? \"\",\n address1: billingAddress?.address1 ?? \"\",\n address2: billingAddress?.address2 ?? \"\",\n city: billingAddress?.city ?? \"\",\n state: billingAddress?.state ?? \"\",\n zip: billingAddress?.zip ?? \"\",\n country_code: billingAddress?.country_code ?? \"US\",\n },\n set_as_default: paymentMethod.default,\n });\n }\n }, [paymentMethod, billingAddress, isOpen, reset]);\n\n const handleCountrySelect = (countryCode: string) => {\n handleCountryChange(countryCode);\n setValue(\"billing_address.country_code\", countryCode, {\n shouldValidate: true,\n });\n };\n\n const handleFormSubmit = handleSubmit((data) => {\n onSubmit(data);\n });\n\n const deleteButtonLabel = useMemo(() => {\n if (isDeleting) return t(\"deleting\");\n if (confirmDelete) return t(\"tap_to_confirm\");\n return t(\"delete_card\");\n }, [isDeleting, confirmDelete, t]);\n\n if (!paymentMethod) return null;\n\n const { details } = paymentMethod;\n const displayName = getCardDisplayName(paymentMethod, t(\"card_fallback\"));\n const expMonth = details.exp_month;\n const expYear = details.exp_year;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"max-h-[90vh] max-w-sm overflow-y-auto md:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t(\"edit_card\")}</DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"bg-muted rounded-lg p-4\">\n <div className=\"flex items-center gap-x-3\">\n <div className=\"h-6 w-10 flex-shrink-0\">\n <PaymentIcon\n logoUrl={details.logo_url}\n brand={getPaymentMethodLogoBrand(paymentMethod)}\n alt={displayName}\n />\n </div>\n <div className=\"flex flex-col\">\n <span className=\"text-foreground text-sm font-medium\">\n {displayName}\n </span>\n {expMonth != null && expYear != null && (\n <span className=\"text-muted-foreground text-sm\">\n {t(\"card_expires\")} {expMonth}/{expYear}\n </span>\n )}\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"text-foreground mb-3 text-sm font-medium\">\n {t(\"billing_address\")}\n </h3>\n <div className=\"space-y-3\">\n <FormSelectField\n control={control}\n name=\"billing_address.country_code\"\n label={t(\"country\")}\n options={countryOptions}\n placeholder={t(\"select_an_option\")}\n onChange={(val) => {\n handleCountrySelect(val);\n }}\n />\n <FormTextField\n control={control}\n name=\"billing_address.name\"\n label={t(\"name\")}\n />\n <FormTextField\n control={control}\n name=\"billing_address.address1\"\n label={t(\"address_line_1\")}\n />\n <FormTextField\n control={control}\n name=\"billing_address.address2\"\n label={t(\"address_line_2\")}\n />\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <FormTextField\n control={control}\n name=\"billing_address.city\"\n label={t(\"city\")}\n />\n <FormSelectField\n control={control}\n name=\"billing_address.state\"\n label={\n config?.regionLabel\n ? t(config.regionLabel as never)\n : t(\"state\")\n }\n options={stateSelectOptions}\n placeholder={t(\"select_state\")}\n />\n <FormTextField\n control={control}\n name=\"billing_address.zip\"\n label={\n config?.postalLabel\n ? t(config.postalLabel as never)\n : t(\"zip_code\")\n }\n />\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"mt-2 flex items-center gap-x-2\">\n <input\n type=\"checkbox\"\n id=\"set_as_default\"\n checked={setAsDefault}\n onChange={() => setValue(\"set_as_default\", !setAsDefault)}\n className=\"size-4\"\n />\n <label htmlFor=\"set_as_default\" className=\"text-sm\">\n {t(\"set_as_default_payment_method\")}\n </label>\n </div>\n\n {error && <div className=\"text-destructive text-sm\">{error}</div>}\n\n <DialogFooter className=\"flex flex-row items-center justify-between gap-2\">\n {onDelete ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => {\n if (confirmDelete) {\n onDelete();\n } else {\n setConfirmDelete(true);\n }\n }}\n disabled={isSubmitting || isDeleting}\n className=\"text-destructive hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2 className=\"size-4\" />\n {deleteButtonLabel}\n </Button>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={onClose}\n disabled={isSubmitting || isDeleting}\n >\n {t(\"cancel\")}\n </Button>\n <Button\n type=\"button\"\n onClick={handleFormSubmit}\n disabled={isSubmitting || isDeleting}\n >\n {isSubmitting && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isSubmitting ? t(\"saving\") : t(\"save_changes\")}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import type React from \"react\";\nimport { useController, type Control } from \"react-hook-form\";\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Label,\n Textarea,\n cn,\n} from \"@fluid-app/ui-primitives\";\nimport { z } from \"zod\";\nimport { FormTextField } from \"./form-fields\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\nfunction normalizeUrl(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (/^https?:\\/\\//i.test(trimmed)) return trimmed;\n return `https://${trimmed}`;\n}\n\nconst urlField = z\n .string()\n .transform(normalizeUrl)\n .pipe(z.string().url().or(z.literal(\"\")));\n\nexport const mySiteProfileSchema = z.object({\n bio: z.string().optional(),\n linkedin: urlField,\n facebook: urlField,\n twitter: urlField,\n instagram: urlField,\n youtube: urlField,\n pinterest: urlField,\n tiktok: urlField,\n whatsapp: z.string().optional(),\n wechat: z.string().optional(),\n});\n\nexport type MySiteProfileFormData = z.infer<typeof mySiteProfileSchema>;\n\nexport const socialFields = [\n {\n name: \"linkedin\",\n label: \"LinkedIn\",\n placeholder: \"linkedin.com/in/username\",\n },\n { name: \"facebook\", label: \"Facebook\", placeholder: \"facebook.com/username\" },\n { name: \"twitter\", label: \"X (Twitter)\", placeholder: \"x.com/username\" },\n {\n name: \"instagram\",\n label: \"Instagram\",\n placeholder: \"instagram.com/username\",\n },\n { name: \"youtube\", label: \"YouTube\", placeholder: \"youtube.com/@channel\" },\n {\n name: \"pinterest\",\n label: \"Pinterest\",\n placeholder: \"pinterest.com/username\",\n },\n { name: \"tiktok\", label: \"TikTok\", placeholder: \"tiktok.com/@username\" },\n { name: \"whatsapp\", label: \"WhatsApp\", placeholder: \"Phone number or link\" },\n { name: \"wechat\", label: \"WeChat\", placeholder: \"WeChat ID\" },\n] as const satisfies readonly {\n name: keyof Omit<MySiteProfileFormData, \"bio\">;\n label: string;\n placeholder: string;\n}[];\n\nexport interface MySiteProfileSectionProps {\n avatarUrl?: string | null;\n initial?: string;\n control: Control<MySiteProfileFormData>;\n}\n\nfunction BioField({ control }: { control: Control<MySiteProfileFormData> }) {\n const { t } = useProfileTranslation();\n const {\n field,\n fieldState: { error },\n } = useController({ name: \"bio\", control });\n return (\n <div className=\"space-y-1\">\n <Label htmlFor=\"mysite-bio\" className=\"block text-sm font-medium\">\n {t(\"bio\")}\n </Label>\n <Textarea\n id=\"mysite-bio\"\n rows={4}\n placeholder={t(\"bio_placeholder\")}\n className={cn(error && \"ring-destructive ring-1\")}\n {...field}\n value={field.value ?? \"\"}\n />\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n\nexport function MySiteProfileSection({\n avatarUrl,\n initial,\n control,\n}: MySiteProfileSectionProps): React.JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <Card>\n <CardHeader>\n <CardTitle>{t(\"about_me\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-col gap-6 lg:flex-row lg:gap-10\">\n <div className=\"bg-muted relative h-[120px] w-[120px] shrink-0 self-center overflow-hidden rounded-full lg:self-auto\">\n {avatarUrl ? (\n <img\n src={avatarUrl}\n alt={t(\"profile_image_alt\")}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"bg-background text-foreground flex h-full w-full items-center justify-center text-xl font-semibold\">\n {initial ?? \"U\"}\n </div>\n )}\n </div>\n\n <div className=\"min-w-0 flex-1\">\n <BioField control={control} />\n </div>\n </CardContent>\n </Card>\n );\n}\n\nexport interface MySiteProfileSocialSectionProps {\n control: Control<MySiteProfileFormData>;\n}\n\nexport function MySiteProfileSocialSection({\n control,\n}: MySiteProfileSocialSectionProps): React.JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <Card>\n <CardHeader>\n <CardTitle>{t(\"social_media_links\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n {socialFields.map((field) => {\n let placeholder: string = field.placeholder;\n if (field.name === \"whatsapp\")\n placeholder = t(\"whatsapp_placeholder\");\n if (field.name === \"wechat\") placeholder = t(\"wechat_placeholder\");\n return (\n <FormTextField\n key={field.name}\n control={control}\n name={field.name}\n label={field.label}\n placeholder={placeholder}\n />\n );\n })}\n </div>\n </CardContent>\n </Card>\n );\n}\n\nexport interface MySiteProfileSaveButtonProps {\n isDirty: boolean;\n isPending: boolean;\n onSubmit: () => void;\n}\n\nexport function MySiteProfileSaveButton({\n isDirty,\n isPending,\n onSubmit,\n}: MySiteProfileSaveButtonProps): React.JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <div className=\"flex justify-end pb-6\">\n <Button\n type=\"button\"\n onClick={onSubmit}\n disabled={!isDirty || isPending}\n className=\"w-full sm:w-auto\"\n >\n {isPending && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isPending ? t(\"saving\") : t(\"save\")}\n </Button>\n </div>\n );\n}\n","\"use client\";\n\nimport type { Control, FieldPath, FieldValues } from \"react-hook-form\";\nimport { useController } from \"react-hook-form\";\nimport { Input, Label, cn } from \"@fluid-app/ui-primitives\";\n\ninterface FormTextFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n control: Control<TFieldValues>;\n name: TName;\n label?: string;\n placeholder?: string;\n containerClassName?: string;\n type?: React.HTMLInputTypeAttribute;\n disabled?: boolean;\n inputMode?: React.HTMLAttributes<HTMLInputElement>[\"inputMode\"];\n autoComplete?: string;\n}\n\nexport function FormTextField<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n control,\n name,\n label,\n placeholder,\n containerClassName,\n type,\n disabled,\n inputMode,\n autoComplete,\n}: FormTextFieldProps<TFieldValues, TName>) {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Input\n {...field}\n value={field.value ?? \"\"}\n id={name}\n type={type}\n disabled={disabled}\n inputMode={inputMode}\n placeholder={placeholder}\n autoComplete={autoComplete}\n aria-invalid={!!error}\n />\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport type { Control, FieldPath, FieldValues } from \"react-hook-form\";\nimport { Controller } from \"react-hook-form\";\nimport { Label, cn } from \"@fluid-app/ui-primitives\";\nimport { Combobox } from \"@fluid-app/ui-components/components/Combobox\";\n\ntype NameValue = { name: string; value: string | number };\n\ninterface FormComboboxFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n control: Control<TFieldValues>;\n name: TName;\n label?: string;\n placeholder?: string;\n emptyText?: string;\n options?: NameValue[];\n containerClassName?: string;\n disabled?: boolean;\n}\n\nexport function FormComboboxField<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n control,\n name,\n label,\n placeholder = \"Select\",\n emptyText = \"No results found.\",\n options,\n containerClassName,\n disabled,\n}: FormComboboxFieldProps<TFieldValues, TName>) {\n const comboboxOptions = useMemo(\n () =>\n options?.map((opt) => ({\n label: opt.name,\n value: opt.value.toString(),\n })) ?? [],\n [options],\n );\n\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Controller\n control={control}\n name={name}\n render={({ field: { onChange, value }, fieldState: { error } }) => (\n <>\n <Combobox\n options={comboboxOptions}\n value={value?.toString() ?? \"\"}\n onValueChange={onChange}\n placeholder={placeholder}\n emptyText={emptyText}\n disabled={disabled}\n />\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </>\n )}\n />\n </div>\n );\n}\n","import {\n type JSX,\n type ReactNode,\n useEffect,\n useId,\n useMemo,\n useState,\n} from \"react\";\nimport {\n Controller,\n useWatch,\n type Control,\n type UseFormSetValue,\n} from \"react-hook-form\";\nimport { Trash2 } from \"lucide-react\";\nimport { z } from \"zod\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Button,\n Label,\n cn,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport { FormTextField } from \"./form-fields/FormTextField\";\nimport { FormComboboxField } from \"./form-fields/FormComboboxField\";\nimport { PhoneInput } from \"@fluid-app/ui-components/components/PhoneInput\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport { useCountryStates, type State } from \"@fluid-app/fluid-pay-core\";\nimport { useAddressFieldsApiOptional } from \"@fluid-app/address-fields/address-fields-api-context\";\nimport type { AddressField } from \"@fluid-app/address-fields/types\";\n\nconst addressFormSchema = z.object({\n name: z.string(),\n address1: z.string(),\n address2: z.string().optional().nullable(),\n city: z.string(),\n state: z.string(),\n postal_code: z.string(),\n phone: z.string().optional().nullable(),\n country_code: z.string().min(1, \"Country is required\"),\n default: z.boolean(),\n});\n\nexport type AddressFormData = z.infer<typeof addressFormSchema>;\n\ninterface CountryOption {\n iso: string;\n name: string;\n}\n\nexport interface AddressAutocompleteRenderProps {\n control: Control<AddressFormData>;\n setValue: UseFormSetValue<AddressFormData>;\n countryCode: string;\n stateOptions: readonly State[];\n}\n\ninterface AddressFormDialogProps {\n isOpen: boolean;\n onClose: () => void;\n selectedAddress: fluidPay.CustomerAddress | null;\n onSubmit: (data: fluidPay.CreateAddressBody) => void;\n isSubmitting: boolean;\n countries?: CountryOption[];\n error?: string;\n renderAddressAutocomplete?: (\n props: AddressAutocompleteRenderProps,\n ) => ReactNode;\n fetchStates?: (countryCode: string) => Promise<State[]>;\n t: (key: string) => string;\n onDelete?: () => void;\n isDeleting?: boolean;\n defaultCountry?: string;\n languageIso?: string;\n}\n\ntype AddressFieldName = keyof Omit<AddressFormData, \"default\" | \"country_code\">;\n\nconst STATIC_FIELD_ORDER: AddressFieldName[] = [\n \"name\",\n \"address1\",\n \"address2\",\n \"city\",\n \"state\",\n \"postal_code\",\n \"phone\",\n];\n\nconst STATIC_LABELS: Record<AddressFieldName, string> = {\n name: \"name\",\n address1: \"address_line_1\",\n address2: \"address_line_2\",\n city: \"city\",\n state: \"state\",\n postal_code: \"zip_code\",\n phone: \"phone_number\",\n};\n\nconst DEFAULT_HIDDEN_FIELDS = new Set<AddressFieldName>([\"phone\"]);\nconst STATIC_REQUIRED_FIELDS = new Set<AddressFieldName>([\n \"name\",\n \"address1\",\n \"city\",\n \"state\",\n \"postal_code\",\n]);\nconst REQUIRED_FIELD_MESSAGES: Record<AddressFieldName, string> = {\n name: \"Name is required\",\n address1: \"Address line 1 is required\",\n address2: \"Address line 2 is required\",\n city: \"City is required\",\n state: \"State is required\",\n postal_code: \"Postal code is required\",\n phone: \"Phone is required\",\n};\nconst AUTOCOMPLETE_FIELD_PREFIX: readonly AddressFieldName[] = [\n \"name\",\n \"address1\",\n \"address2\",\n];\n\nfunction isAddressFieldName(value: string): value is AddressFieldName {\n return STATIC_FIELD_ORDER.includes(value as AddressFieldName);\n}\n\nfunction prioritizeAutocompleteFields(\n fields: readonly AddressFieldName[],\n): AddressFieldName[] {\n const prefix = AUTOCOMPLETE_FIELD_PREFIX.filter((field) =>\n fields.includes(field),\n );\n const rest = fields.filter(\n (field) => !AUTOCOMPLETE_FIELD_PREFIX.includes(field),\n );\n return [...prefix, ...rest];\n}\n\nfunction groupFields(fields: AddressFieldName[]): AddressFieldName[][] {\n const groups: AddressFieldName[][] = [];\n let i = 0;\n while (i < fields.length) {\n const current = fields[i]!;\n\n const locationFields = new Set<AddressFieldName>([\n \"city\",\n \"state\",\n \"postal_code\",\n ]);\n if (locationFields.has(current)) {\n const group: AddressFieldName[] = [current];\n while (\n i + group.length < fields.length &&\n locationFields.has(fields[i + group.length]!)\n ) {\n group.push(fields[i + group.length]!);\n }\n if (group.length > 1) {\n groups.push(group);\n i += group.length;\n continue;\n }\n }\n\n groups.push([current]);\n i++;\n }\n return groups;\n}\n\nfunction isFieldRequired(\n bffFields: readonly AddressField[] | null,\n fieldName: AddressFieldName,\n): boolean {\n if (!bffFields) return STATIC_REQUIRED_FIELDS.has(fieldName);\n return (\n bffFields.find((field) => field.field === fieldName)?.required ?? false\n );\n}\n\nfunction isBlank(value: string | null | undefined): boolean {\n return value == null || value.trim().length === 0;\n}\n\nexport default function AddressFormDialog({\n isOpen,\n onClose,\n selectedAddress,\n onSubmit,\n isSubmitting,\n countries = [],\n error,\n renderAddressAutocomplete,\n fetchStates,\n t,\n onDelete,\n isDeleting = false,\n defaultCountry = \"US\",\n languageIso,\n}: AddressFormDialogProps): JSX.Element {\n const [confirmDelete, setConfirmDelete] = useState(false);\n const phoneInputId = useId();\n const addressFieldsApi = useAddressFieldsApiOptional();\n const [bffFields, setBffFields] = useState<AddressField[] | null>(null);\n\n useEffect(() => {\n if (!isOpen) setConfirmDelete(false);\n }, [isOpen]);\n\n useEffect(() => {\n if (!confirmDelete) return;\n const timeoutId = setTimeout(() => setConfirmDelete(false), 3000);\n return () => clearTimeout(timeoutId);\n }, [confirmDelete]);\n\n const deleteButtonLabel = useMemo(() => {\n if (isDeleting) return t(\"deleting\");\n if (confirmDelete) return t(\"tap_to_confirm\");\n return t(\"delete_address\");\n }, [isDeleting, confirmDelete, t]);\n\n const { control, handleSubmit, reset, setValue, setError, clearErrors } =\n useZodForm<AddressFormData>(addressFormSchema, {\n defaultValues: {\n name: \"\",\n address1: \"\",\n address2: \"\",\n city: \"\",\n state: \"\",\n postal_code: \"\",\n phone: \"\",\n country_code: defaultCountry,\n default: false,\n },\n });\n\n const selectedCountryCode = useWatch({ control, name: \"country_code\" });\n const isDefault = useWatch({ control, name: \"default\" });\n\n const { stateOptions, config, handleCountryChange } = useCountryStates();\n const hasAddressAutocomplete = Boolean(renderAddressAutocomplete);\n\n useEffect(() => {\n if (!addressFieldsApi || !selectedCountryCode) return;\n let cancelled = false;\n setBffFields(null);\n addressFieldsApi\n .fetchAddressFields(selectedCountryCode, languageIso)\n .then((response) => {\n if (!cancelled) {\n setBffFields([...response.address_fields.fields]);\n }\n })\n .catch(() => {\n if (!cancelled) setBffFields(null);\n });\n return () => {\n cancelled = true;\n };\n }, [addressFieldsApi, selectedCountryCode, languageIso]);\n\n const fieldOrder = useMemo<AddressFieldName[]>(() => {\n if (!bffFields)\n return STATIC_FIELD_ORDER.filter((f) => !DEFAULT_HIDDEN_FIELDS.has(f));\n const fieldOrder = bffFields.map((f) => f.field).filter(isAddressFieldName);\n return hasAddressAutocomplete\n ? prioritizeAutocompleteFields(fieldOrder)\n : fieldOrder;\n }, [bffFields, hasAddressAutocomplete]);\n\n const fieldGroups = useMemo(() => groupFields(fieldOrder), [fieldOrder]);\n\n const getFieldLabel = (fieldName: AddressFieldName): string => {\n if (bffFields) {\n const bffField = bffFields.find((f) => f.field === fieldName);\n if (bffField) return bffField.label;\n }\n if (fieldName === \"state\" && config?.regionLabel) {\n return t(config.regionLabel);\n }\n if (fieldName === \"postal_code\" && config?.postalLabel) {\n return t(config.postalLabel);\n }\n const key = STATIC_LABELS[fieldName];\n return key ? t(key) : fieldName;\n };\n\n const countrySelectOptions = [...countries]\n .map((country) => ({\n name: country.name,\n value: country.iso,\n }))\n .sort((a, b) => {\n if (a.value === selectedCountryCode) return -1;\n if (b.value === selectedCountryCode) return 1;\n return a.name.localeCompare(b.name);\n });\n\n const stateSelectOptions = stateOptions.map((state) => ({\n name: state.name,\n value: state.isoCode,\n }));\n\n useEffect(() => {\n if (isOpen) {\n if (selectedAddress) {\n reset({\n name: selectedAddress.name ?? \"\",\n address1: selectedAddress.address1,\n address2: selectedAddress.address2 ?? \"\",\n city: selectedAddress.city,\n state: selectedAddress.state,\n postal_code: selectedAddress.postal_code,\n phone: selectedAddress.phone ?? \"\",\n country_code: selectedAddress.country_code ?? defaultCountry,\n default: selectedAddress.default,\n });\n handleCountryChange(\n selectedAddress.country_code ?? defaultCountry,\n fetchStates,\n );\n } else {\n reset({\n name: \"\",\n address1: \"\",\n address2: \"\",\n city: \"\",\n state: \"\",\n postal_code: \"\",\n phone: \"\",\n country_code: defaultCountry,\n default: false,\n });\n handleCountryChange(defaultCountry, fetchStates);\n }\n }\n // defaultCountry intentionally omitted: a background addresses refetch can\n // change the parent-computed value while the dialog is open, which would\n // wipe the user's in-progress edits.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedAddress, isOpen, reset, handleCountryChange, fetchStates]);\n\n useEffect(() => {\n handleCountryChange(selectedCountryCode, fetchStates);\n }, [selectedCountryCode, handleCountryChange, fetchStates]);\n\n const handleFormSubmit = handleSubmit((data) => {\n clearErrors(STATIC_FIELD_ORDER);\n const missingRequiredFields = fieldOrder.filter(\n (fieldName) =>\n isFieldRequired(bffFields, fieldName) && isBlank(data[fieldName]),\n );\n\n if (missingRequiredFields.length > 0) {\n for (const fieldName of missingRequiredFields) {\n setError(fieldName, {\n type: \"manual\",\n message: REQUIRED_FIELD_MESSAGES[fieldName],\n });\n }\n return;\n }\n\n const city = bffFields && !fieldOrder.includes(\"city\") ? \"\" : data.city;\n const state = bffFields && !fieldOrder.includes(\"state\") ? \"\" : data.state;\n\n onSubmit({\n address: {\n name: data.name,\n address1: data.address1,\n address2: data.address2 ?? null,\n city,\n state,\n postal_code: data.postal_code,\n phone: data.phone || null,\n country_code: data.country_code,\n default: data.default,\n },\n });\n });\n\n const renderField = (\n fieldName: AddressFieldName,\n containerClassName?: string,\n ): JSX.Element => {\n const label = getFieldLabel(fieldName);\n\n if (fieldName === \"state\" && stateSelectOptions.length > 0) {\n return (\n <FormComboboxField\n key={fieldName}\n control={control}\n name=\"state\"\n label={label}\n placeholder={t(\"select_state\")}\n options={stateSelectOptions}\n containerClassName={containerClassName}\n disabled={isSubmitting}\n />\n );\n }\n\n if (fieldName === \"address1\" && renderAddressAutocomplete) {\n return (\n <div key={fieldName} className={cn(\"space-y-1\", containerClassName)}>\n <Label\n htmlFor={fieldName}\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {label}\n </Label>\n {renderAddressAutocomplete({\n control,\n setValue,\n countryCode: selectedCountryCode,\n stateOptions,\n })}\n </div>\n );\n }\n\n if (fieldName === \"phone\") {\n return (\n <div key={fieldName} className={containerClassName}>\n <Controller\n control={control}\n name=\"phone\"\n render={({ field: { value, onChange }, fieldState: { error } }) => (\n <div className=\"space-y-1\">\n <Label\n htmlFor={phoneInputId}\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {label}\n </Label>\n <PhoneInput\n id={phoneInputId}\n value={value ?? \"\"}\n onChange={onChange}\n defaultCountry={selectedCountryCode}\n error={!!error}\n disabled={isSubmitting}\n searchPlaceholder={t(\"search_country\")}\n emptyText={t(\"no_results_found\")}\n />\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </div>\n )}\n />\n </div>\n );\n }\n\n return (\n <FormTextField\n key={fieldName}\n control={control}\n name={fieldName}\n label={label}\n containerClassName={containerClassName}\n disabled={isSubmitting}\n autoComplete={fieldName === \"name\" ? \"name\" : undefined}\n />\n );\n };\n\n const isEditMode = selectedAddress !== null;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent\n aria-describedby={undefined}\n className=\"max-h-[90vh] max-w-md overflow-y-auto rounded md:max-w-xl\"\n >\n <DialogHeader>\n <DialogTitle className=\"text-lg font-medium\">\n {isEditMode ? t(\"edit_address\") : t(\"add_an_address\")}\n </DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-3 pt-2\">\n <FormComboboxField\n control={control}\n name=\"country_code\"\n label={t(\"country\")}\n placeholder={t(\"select_an_option\")}\n options={countrySelectOptions}\n disabled={isSubmitting}\n />\n {fieldGroups.map((group) => {\n if (group.length === 1) {\n return renderField(group[0]!);\n }\n return (\n <div key={group.join(\"-\")} className=\"flex gap-3\">\n {group.map((fieldName) => renderField(fieldName, \"flex-1\"))}\n </div>\n );\n })}\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n {onDelete ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => {\n if (confirmDelete) {\n onDelete();\n } else {\n setConfirmDelete(true);\n }\n }}\n disabled={isSubmitting || isDeleting}\n className=\"text-destructive hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2 className=\"size-4\" />\n {deleteButtonLabel}\n </Button>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-x-2\">\n <input\n type=\"checkbox\"\n id=\"set_as_default_address\"\n checked={isDefault}\n onChange={() => setValue(\"default\", !isDefault)}\n disabled={isSubmitting || isDeleting}\n className=\"size-4\"\n />\n <label htmlFor=\"set_as_default_address\" className=\"text-sm\">\n {t(\"set_as_default_address\")}\n </label>\n </div>\n\n <Button\n type=\"submit\"\n onClick={handleFormSubmit}\n disabled={isSubmitting || isDeleting}\n >\n {isSubmitting && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isSubmitting ? t(\"saving\") : t(\"save_address\")}\n </Button>\n </div>\n </div>\n\n {error && <div className=\"text-destructive mt-2 text-sm\">{error}</div>}\n </DialogContent>\n </Dialog>\n );\n}\n","import { useEffect, useRef, useState, useCallback } from \"react\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\n\nexport type VgsFieldState = {\n isFocused: boolean;\n isValid: boolean;\n isEmpty: boolean;\n cardType?: string;\n bin?: string;\n last4?: string;\n};\n\nexport type VgsFormState = {\n [key: string]: VgsFieldState;\n};\n\ninterface VGSCollectForm {\n setRouteId: (routeId: string) => void;\n field: (selector: string, config: Record<string, unknown>) => void;\n submit: (\n path: string,\n options: { headers?: Record<string, string> },\n successCallback: (\n status: number,\n response: fluidPay.VgsCollectSubmitResponse,\n ) => void,\n errorCallback: (errors: Record<string, string[]>) => void,\n ) => void;\n unmount: () => void;\n}\n\ninterface VGSCollectLibrary {\n create: (\n vaultId: string,\n environment: string,\n stateCallback: (state: Record<string, Partial<VgsFieldState>>) => void,\n ) => VGSCollectForm;\n}\n\nconst getVGSCollect = (): VGSCollectLibrary | null => {\n if (typeof window !== \"undefined\") {\n return (\n (window as unknown as { VGSCollect?: VGSCollectLibrary }).VGSCollect ||\n null\n );\n }\n return null;\n};\n\nconst DEFAULT_FIELD_STATE: VgsFormState = {\n card_number: { isFocused: false, isValid: false, isEmpty: true },\n card_exp: { isFocused: false, isValid: false, isEmpty: true },\n card_cvc: { isFocused: false, isValid: false, isEmpty: true },\n card_holder: { isFocused: false, isValid: false, isEmpty: true },\n};\n\nexport function useVgsCollect() {\n const [isFormReady, setIsFormReady] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const [isScriptLoaded, setIsScriptLoaded] = useState(false);\n const [scriptLoadError, setScriptLoadError] = useState<string | undefined>(\n undefined,\n );\n\n const [vgsFieldState, setVgsFieldState] =\n useState<VgsFormState>(DEFAULT_FIELD_STATE);\n\n const formRef = useRef<VGSCollectForm | null>(null);\n const onSuccessRef = useRef<\n ((response: fluidPay.VgsCollectSubmitResponse) => void) | null\n >(null);\n const onErrorRef = useRef<((error: string) => void) | null>(null);\n\n // Load VGS Collect script\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n const vgsCollect = getVGSCollect();\n if (vgsCollect) {\n const timeoutId = window.setTimeout(() => {\n setIsScriptLoaded(true);\n }, 0);\n return () => window.clearTimeout(timeoutId);\n }\n\n if (isScriptLoaded) return;\n\n const script = document.createElement(\"script\");\n script.src =\n \"https://js.verygoodvault.com/vgs-collect/3.3.0/vgs-collect.js\";\n script.async = true;\n script.onload = () => setIsScriptLoaded(true);\n // Surface load failures both to a registered consumer (if any) and to\n // state, so a consumer mounting after the failure still sees it.\n script.onerror = () => {\n const message = \"Failed to load payment form\";\n setScriptLoadError(message);\n onErrorRef.current?.(message);\n };\n document.head.appendChild(script);\n }, [isScriptLoaded]);\n\n const updateFieldState = useCallback(\n (state: Record<string, Partial<VgsFieldState>>) => {\n if (!state) return;\n\n setVgsFieldState((prev) => {\n const newState: VgsFormState = { ...prev };\n let hasChanges = false;\n\n Object.keys(state).forEach((key) => {\n const field = state[key];\n if (!field) return;\n\n const next: VgsFieldState = {\n isFocused: !!field.isFocused,\n isValid: !!field.isValid,\n isEmpty: !!field.isEmpty,\n cardType: field.cardType,\n bin: field.bin,\n last4: field.last4,\n };\n\n if (\n !newState[key] ||\n JSON.stringify(newState[key]) !== JSON.stringify(next)\n ) {\n newState[key] = next;\n hasChanges = true;\n }\n });\n\n return hasChanges ? newState : prev;\n });\n },\n [],\n );\n\n const resetVgs = useCallback(() => {\n if (formRef.current) {\n try {\n formRef.current.unmount();\n } catch (e) {\n console.warn(\"VGS unmount error\", e);\n }\n formRef.current = null;\n }\n\n setIsFormReady(false);\n setVgsFieldState({ ...DEFAULT_FIELD_STATE });\n }, []);\n\n const initializeForm = useCallback(\n async (credentials: fluidPay.VaultResponse) => {\n if (!isScriptLoaded) return;\n\n const VGSCollect = getVGSCollect();\n if (!VGSCollect) {\n onErrorRef.current?.(\"Payment script not loaded\");\n return;\n }\n\n try {\n setIsLoading(true);\n\n const vgsEnvironment =\n credentials.vault.environment === \"sandbox\" ? \"sandbox\" : \"live-eu-1\";\n\n const form = VGSCollect.create(\n credentials.vault.vault_id,\n vgsEnvironment,\n (state) => updateFieldState(state),\n );\n\n if (credentials.vault.route_id) {\n form.setRouteId(credentials.vault.route_id);\n }\n\n formRef.current = form;\n\n // Wait for DOM elements\n let retries = 0;\n const maxRetries = 20;\n const retryDelay = 200;\n\n const waitForDom = async (): Promise<boolean> => {\n const allExist = [\n \"vgs-card-number\",\n \"vgs-expiration-date\",\n \"vgs-cvc\",\n \"vgs-card-holder\",\n ].every((id) => {\n const el = document.getElementById(id);\n return el && el.isConnected;\n });\n\n if (allExist) return true;\n if (retries >= maxRetries) return false;\n\n retries++;\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n return waitForDom();\n };\n\n const domReady = await waitForDom();\n if (!domReady) {\n throw new Error(\"Payment form elements not ready\");\n }\n\n // Resolve theme tokens from the wrapper element. The theme engine\n // applies `data-theme` on a subtree element (not <html>), so CSS\n // variables aren't visible on document.documentElement — only on a\n // node inside the themed subtree. Cross-origin iframe contents\n // can't see parent CSS variables, so we resolve concrete values\n // here and pass them to VGS.\n //\n // The `vgs-card-number` element is guaranteed to exist at this\n // point: the `waitForDom` check above blocks until all four VGS\n // wrapper elements are mounted. The hardcoded fallback colors only\n // activate if the CSS variables themselves are unset.\n const themedNode = document.getElementById(\"vgs-card-number\");\n const probeStyle = themedNode ? getComputedStyle(themedNode) : null;\n const foregroundColor =\n probeStyle?.getPropertyValue(\"--color-foreground\").trim() ||\n \"#0a0a0a\";\n const mutedForegroundColor =\n probeStyle?.getPropertyValue(\"--color-muted-foreground\").trim() ||\n \"#737373\";\n\n const css = {\n fontSize: \"16px\",\n fontFamily:\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\",\n color: foregroundColor,\n background: \"transparent\",\n paddingTop: \"0px\",\n paddingBottom: \"0px\",\n paddingLeft: \"0px\",\n paddingRight: \"0px\",\n boxSizing: \"border-box\",\n lineHeight: \"26px\",\n height: \"100%\",\n width: \"100%\",\n \"&::placeholder\": { color: mutedForegroundColor, opacity: 1 },\n \"&:focus\": { outline: \"none\" },\n };\n\n form.field(\"#vgs-card-number\", {\n type: \"card-number\",\n name: \"card_number\",\n placeholder: \"0000 0000 0000 0000\",\n showCardIcon: true,\n css: { ...css, paddingRight: \"40px\" },\n autoComplete: \"cc-number\",\n validations: [\"required\", \"validCardNumber\"],\n });\n\n form.field(\"#vgs-expiration-date\", {\n type: \"card-expiration-date\",\n name: \"card_exp\",\n yearLength: 2,\n placeholder: \"MM / YY\",\n css,\n autoComplete: \"cc-exp\",\n validations: [\"required\", \"validCardExpirationDate\"],\n });\n\n form.field(\"#vgs-cvc\", {\n type: \"card-security-code\",\n name: \"card_cvc\",\n placeholder: \"123\",\n css,\n autoComplete: \"cc-csc\",\n validations: [\"required\", \"validCardSecurityCode\"],\n });\n\n form.field(\"#vgs-card-holder\", {\n type: \"text\",\n name: \"card_holder\",\n placeholder: \"Full Name\",\n css,\n autoComplete: \"cc-name\",\n validations: [\"required\"],\n });\n\n setIsFormReady(true);\n } catch (error) {\n console.error(\"Failed to initialize VGS:\", error);\n onErrorRef.current?.(\"Failed to initialize payment form\");\n } finally {\n setIsLoading(false);\n }\n },\n [isScriptLoaded, updateFieldState],\n );\n\n const submitCard = useCallback(() => {\n if (!formRef.current) {\n onErrorRef.current?.(\"Form not initialized\");\n return;\n }\n\n formRef.current.submit(\n `/post`,\n {},\n (status: number, response: fluidPay.VgsCollectSubmitResponse) => {\n if (status >= 200 && status < 400) {\n onSuccessRef.current?.(response);\n } else {\n onErrorRef.current?.(\"Payment failed\");\n }\n },\n (errors: Record<string, string[]>) => {\n const allErrors = Object.values(errors)\n .flat()\n .filter(\n (msg) =>\n typeof msg === \"string\" &&\n msg.trim().length > 0 &&\n msg !== \"Required\",\n );\n\n const uniqueErrors = Array.from(new Set(allErrors));\n\n let errorMsg = \"Please verify your payment details.\";\n if (uniqueErrors.length > 0) {\n errorMsg = uniqueErrors.join(\", \");\n } else {\n const rawErrors = Object.values(errors).flat();\n if (rawErrors.length > 0) {\n errorMsg = \"Please fill in all required fields.\";\n }\n }\n\n onErrorRef.current?.(errorMsg);\n },\n );\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (formRef.current) {\n try {\n formRef.current.unmount();\n } catch (e) {\n console.warn(\"VGS unmount error\", e);\n }\n formRef.current = null;\n }\n };\n }, []);\n\n return {\n isFormReady,\n isLoading,\n isScriptLoaded,\n scriptLoadError,\n initializeForm,\n submitCard,\n vgsFieldState,\n resetVgs,\n onSuccessRef,\n onErrorRef,\n };\n}\n","import {\n type JSX,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { Controller } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Button,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n cn,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport { useFluidPayApi } from \"@fluid-app/fluid-pay-core\";\nimport {\n useVgsCollect,\n type VgsFieldState,\n type VgsFormState,\n} from \"../hooks/use-vgs-collect\";\n\nconst SENTINEL_CREATE_NEW = \"__create_new__\";\n\nconst formSchema = z.object({\n billing_address_selection: z\n .string()\n .refine((value) => value !== \"\" && value !== SENTINEL_CREATE_NEW, {\n message: \"Please select a billing address\",\n }),\n set_as_default: z.boolean(),\n});\n\ntype FormData = z.infer<typeof formSchema>;\n\nexport interface CreditCardFormSubmitData {\n set_as_default: boolean;\n payment_method: {\n token: string;\n cvv_token: string;\n exp_month: string;\n exp_year: string;\n card_holder: string;\n bin?: string;\n last_four?: string;\n card_network?: string;\n };\n billing_address_id: number;\n}\n\nexport interface RenderAddressFormDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onAddressCreated: (address: fluidPay.CustomerAddress) => void;\n}\n\ninterface CreditCardFormDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: CreditCardFormSubmitData) => void;\n isSubmitting: boolean;\n error?: string;\n jwt: string;\n /** When provided, adds a top \"Same as shipping\" option referencing this address. */\n shippingAddress?: fluidPay.CustomerAddress | null;\n /** Saved customer addresses populating the dropdown. */\n savedAddresses?: fluidPay.CustomerAddress[];\n /** ID of the customer's preferred billing address; used to pre-select. */\n defaultBillingAddressId?: number;\n /**\n * Render-prop slot for AddressFormDialog. The host wires the dialog so this\n * component doesn't import it directly, keeping the address-create flow's\n * data plumbing (mutations, error toasts) under host control.\n */\n renderAddressFormDialog?: (props: RenderAddressFormDialogProps) => ReactNode;\n t: (key: string) => string;\n}\n\nfunction VgsInputWrapper({\n id,\n label,\n fieldState,\n className,\n}: {\n id: string;\n label: string;\n fieldState?: VgsFieldState;\n className?: string;\n}) {\n const isInvalid =\n fieldState?.isValid === false &&\n !fieldState?.isFocused &&\n !fieldState?.isEmpty;\n const isFocused = !!fieldState?.isFocused;\n\n return (\n <div\n id={id}\n role=\"group\"\n aria-label={label}\n data-invalid={isInvalid ? \"\" : undefined}\n className={cn(\n \"border-input selection:bg-primary selection:text-primary-foreground file:text-foreground placeholder:text-muted-foreground h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n \"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n \"data-[invalid]:border-destructive data-[invalid]:ring-destructive/20\",\n \"[&_iframe]:block! [&_iframe]:h-full! [&_iframe]:w-full! [&_iframe]:border-0!\",\n isFocused && \"border-ring ring-ring/50 ring-[3px]\",\n isFocused && isInvalid && \"border-destructive ring-destructive/20\",\n className,\n )}\n />\n );\n}\n\nfunction VgsCardForm({\n vgsFieldState,\n networkError,\n}: {\n vgsFieldState: VgsFormState;\n networkError?: string;\n}) {\n return (\n <div className=\"space-y-4\" aria-label=\"Credit card form\">\n <VgsInputWrapper\n id=\"vgs-card-number\"\n label=\"Card number\"\n fieldState={vgsFieldState[\"card_number\"]}\n />\n\n <div className=\"flex gap-3\">\n <div className=\"flex-1\">\n <VgsInputWrapper\n id=\"vgs-expiration-date\"\n label=\"Expiration date\"\n fieldState={vgsFieldState[\"card_exp\"]}\n />\n </div>\n <div className=\"flex-1\">\n <VgsInputWrapper\n id=\"vgs-cvc\"\n label=\"CVC\"\n fieldState={vgsFieldState[\"card_cvc\"]}\n />\n </div>\n </div>\n\n <VgsInputWrapper\n id=\"vgs-card-holder\"\n label=\"Cardholder name\"\n fieldState={vgsFieldState[\"card_holder\"]}\n />\n\n {networkError && networkError.trim().length > 0 && (\n <div className=\"bg-destructive/10 text-destructive rounded-md p-3 text-sm\">\n {networkError}\n </div>\n )}\n </div>\n );\n}\n\nfunction formatAddressOneLine(addr: fluidPay.CustomerAddress): string {\n const cityStateZip = [addr.city, addr.state, addr.postal_code]\n .filter((p): p is string => Boolean(p && p.trim().length > 0))\n .join(\", \");\n const parts = [\n addr.address1,\n addr.address2,\n cityStateZip,\n addr.country_code,\n ].filter((p): p is string => Boolean(p && p.trim().length > 0));\n return parts.join(\" • \");\n}\n\nfunction AddressSummaryCard({\n address,\n}: {\n address: fluidPay.CustomerAddress;\n}) {\n return (\n <div className=\"border-input bg-muted/30 rounded-md border p-3 text-sm break-words\">\n {address.name && <div className=\"font-medium\">{address.name}</div>}\n <div className=\"text-muted-foreground\">{address.address1}</div>\n {address.address2 && (\n <div className=\"text-muted-foreground\">{address.address2}</div>\n )}\n <div className=\"text-muted-foreground\">\n {[address.city, address.state].filter(Boolean).join(\", \")}\n {address.postal_code ? ` ${address.postal_code}` : \"\"}\n </div>\n {address.country_code && (\n <div className=\"text-muted-foreground\">{address.country_code}</div>\n )}\n </div>\n );\n}\n\nexport default function CreditCardFormDialog({\n isOpen,\n onClose,\n onSubmit,\n isSubmitting,\n error,\n jwt,\n shippingAddress,\n savedAddresses = [],\n defaultBillingAddressId,\n renderAddressFormDialog,\n t,\n}: CreditCardFormDialogProps): JSX.Element {\n const api = useFluidPayApi();\n const [cardError, setCardError] = useState<string | undefined>(undefined);\n const [isVgsSubmitting, setIsVgsSubmitting] = useState(false);\n const [createdAddresses, setCreatedAddresses] = useState<\n fluidPay.CustomerAddress[]\n >([]);\n const [isAddressDialogOpen, setIsAddressDialogOpen] = useState(false);\n\n const {\n isFormReady,\n isLoading: isVgsLoading,\n isScriptLoaded,\n scriptLoadError,\n initializeForm,\n submitCard,\n vgsFieldState,\n resetVgs,\n onSuccessRef,\n onErrorRef,\n } = useVgsCollect();\n\n const shippingId = shippingAddress?.id ?? null;\n const dedupedSavedAddresses = useMemo(\n () =>\n shippingId === null\n ? savedAddresses\n : savedAddresses.filter((a) => a.id !== shippingId),\n [savedAddresses, shippingId],\n );\n\n const allAddresses = useMemo<fluidPay.CustomerAddress[]>(() => {\n const list: fluidPay.CustomerAddress[] = [];\n if (shippingAddress) list.push(shippingAddress);\n list.push(...dedupedSavedAddresses);\n list.push(...createdAddresses);\n return list;\n }, [shippingAddress, dedupedSavedAddresses, createdAddresses]);\n\n const hasRealAddresses = allAddresses.length > 0;\n\n // Default selection per ticket: shipping → default billing → first saved → \"use different\".\n const initialSelection = useMemo<string>(() => {\n if (shippingAddress) return shippingAddress.id.toString();\n if (\n defaultBillingAddressId &&\n dedupedSavedAddresses.some((a) => a.id === defaultBillingAddressId)\n ) {\n return defaultBillingAddressId.toString();\n }\n const first = dedupedSavedAddresses[0];\n if (first) return first.id.toString();\n return SENTINEL_CREATE_NEW;\n }, [shippingAddress, defaultBillingAddressId, dedupedSavedAddresses]);\n\n const { control, handleSubmit, reset, setValue, watch } =\n useZodForm<FormData>(formSchema, {\n defaultValues: {\n billing_address_selection: initialSelection,\n set_as_default: false,\n },\n });\n\n // Reset the form whenever the dialog (re)opens or the upstream address list\n // changes — the initial selection depends on shipping/saved/default props.\n useEffect(() => {\n if (isOpen) {\n reset({\n billing_address_selection: initialSelection,\n set_as_default: false,\n });\n setCreatedAddresses([]);\n }\n }, [isOpen, initialSelection, reset]);\n\n const currentSelection = watch(\"billing_address_selection\");\n const setAsDefault = watch(\"set_as_default\");\n\n const selectedAddress = useMemo(() => {\n if (!currentSelection || currentSelection === SENTINEL_CREATE_NEW) {\n return null;\n }\n const id = Number(currentSelection);\n if (!Number.isFinite(id)) return null;\n return allAddresses.find((a) => a.id === id) ?? null;\n }, [currentSelection, allAddresses]);\n\n const selectOptions = useMemo(() => {\n const options: { name: string; value: string }[] = [];\n if (shippingAddress) {\n options.push({\n name: t(\"same_as_shipping\"),\n value: shippingAddress.id.toString(),\n });\n }\n for (const addr of dedupedSavedAddresses) {\n options.push({\n name: formatAddressOneLine(addr),\n value: addr.id.toString(),\n });\n }\n for (const addr of createdAddresses) {\n options.push({\n name: formatAddressOneLine(addr),\n value: addr.id.toString(),\n });\n }\n options.push({\n name: t(\"use_a_different_address\"),\n value: SENTINEL_CREATE_NEW,\n });\n return options;\n }, [shippingAddress, dedupedSavedAddresses, createdAddresses, t]);\n\n const handleSelectionChange = useCallback(\n (value: string) => {\n if (value === SENTINEL_CREATE_NEW) {\n // Don't commit the sentinel to form state — opening the sub-modal is\n // the action. The form's previous selection remains intact so closing\n // the sub-modal without saving leaves the dropdown unchanged.\n setIsAddressDialogOpen(true);\n return;\n }\n setValue(\"billing_address_selection\", value, { shouldValidate: true });\n },\n [setValue],\n );\n\n const handleAddressCreated = useCallback(\n (address: fluidPay.CustomerAddress) => {\n setCreatedAddresses((list) => [...list, address]);\n setValue(\"billing_address_selection\", address.id.toString(), {\n shouldValidate: true,\n });\n setIsAddressDialogOpen(false);\n },\n [setValue],\n );\n\n const handleVgsSuccess = useCallback(\n (response: fluidPay.VgsCollectSubmitResponse) => {\n const expValue = response?.card_exp;\n if (!expValue) {\n setCardError(\"Missing expiration date from payment form\");\n setIsVgsSubmitting(false);\n return;\n }\n\n const cleanExp = expValue.replace(/\\s+/g, \"\");\n const [expMonth, expYearShort] = cleanExp.split(\"/\");\n\n if (!expMonth || !expYearShort) {\n setCardError(\"Invalid expiration date format\");\n setIsVgsSubmitting(false);\n return;\n }\n\n const expYear =\n expYearShort.length === 2 ? `20${expYearShort}` : expYearShort;\n\n const cardholderName = response?.card_holder?.trim();\n if (!cardholderName) {\n setCardError(\"Cardholder name is required\");\n setIsVgsSubmitting(false);\n return;\n }\n\n handleSubmit(\n (formData) => {\n const billingAddressId = Number(formData.billing_address_selection);\n if (!Number.isFinite(billingAddressId)) {\n setCardError(\"Please select a billing address.\");\n setIsVgsSubmitting(false);\n return;\n }\n\n const cardMeta = vgsFieldState[\"card_number\"];\n onSubmit({\n set_as_default: formData.set_as_default,\n payment_method: {\n token: response.card_number,\n cvv_token: response.card_cvc,\n exp_month: expMonth,\n exp_year: expYear,\n card_holder: cardholderName,\n ...(cardMeta?.bin ? { bin: cardMeta.bin } : {}),\n ...(cardMeta?.last4 ? { last_four: cardMeta.last4 } : {}),\n ...(cardMeta?.cardType\n ? { card_network: cardMeta.cardType }\n : {}),\n },\n billing_address_id: billingAddressId,\n });\n setIsVgsSubmitting(false);\n },\n () => {\n setCardError(\"Please select a billing address.\");\n setIsVgsSubmitting(false);\n },\n )();\n },\n [handleSubmit, onSubmit, vgsFieldState],\n );\n\n const handleVgsError = useCallback((err: string) => {\n setCardError(err);\n setIsVgsSubmitting(false);\n }, []);\n\n useEffect(() => {\n onSuccessRef.current = handleVgsSuccess;\n onErrorRef.current = handleVgsError;\n }, [handleVgsSuccess, handleVgsError, onSuccessRef, onErrorRef]);\n\n useEffect(() => {\n if (!isOpen || !isScriptLoaded) return;\n\n let cancelled = false;\n\n const init = async () => {\n try {\n const credentials = await api.vault.fetchCredentials(jwt);\n if (!cancelled) {\n await initializeForm(credentials);\n }\n } catch (err) {\n console.error(\"Failed to fetch vault credentials:\", err);\n if (!cancelled) {\n setCardError(\"Failed to initialize payment form\");\n }\n }\n };\n\n init();\n\n return () => {\n cancelled = true;\n };\n }, [isOpen, isScriptLoaded, jwt, api, initializeForm]);\n\n const handleClose = () => {\n resetVgs();\n reset();\n setCardError(undefined);\n setIsVgsSubmitting(false);\n setIsAddressDialogOpen(false);\n onClose();\n };\n\n const handleFormSubmit = handleSubmit(() => {\n setIsVgsSubmitting(true);\n setCardError(undefined);\n submitCard();\n });\n\n const isBusy = isSubmitting || isVgsSubmitting;\n\n return (\n <>\n <Dialog open={isOpen} onOpenChange={(open) => !open && handleClose()}>\n <DialogContent className=\"max-h-[90vh] max-w-md overflow-y-auto p-0 md:max-w-xl\">\n <div className=\"relative min-w-0 p-6\">\n {(isVgsLoading || !isFormReady) && !scriptLoadError && (\n <div className=\"bg-background/80 absolute inset-0 z-10 flex items-center justify-center rounded backdrop-blur-[1px]\">\n <div className=\"flex items-center justify-center\">\n <div className=\"border-primary mr-3 size-5 animate-spin rounded-full border-2 border-t-2 border-t-transparent\" />\n <p className=\"text-muted-foreground text-sm font-medium\">\n {t(\"securing\")}\n </p>\n </div>\n </div>\n )}\n <DialogHeader>\n <DialogTitle>{t(\"add_credit_card\")}</DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-4 pt-6\">\n <VgsCardForm\n vgsFieldState={vgsFieldState}\n networkError={cardError ?? scriptLoadError ?? error}\n />\n\n <div className=\"space-y-3 pt-2\">\n <h3 className=\"text-muted-foreground text-sm font-medium\">\n {t(\"billing_address\")}\n </h3>\n {hasRealAddresses ? (\n <Controller\n control={control}\n name=\"billing_address_selection\"\n render={({\n field: { value },\n fieldState: { error: fieldError },\n }) => (\n <div className=\"space-y-1\">\n <Label\n htmlFor=\"billing-address-selection\"\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {t(\"billing_address\")}\n </Label>\n <Select\n value={value}\n onValueChange={handleSelectionChange}\n disabled={isBusy}\n >\n <SelectTrigger\n id=\"billing-address-selection\"\n aria-invalid={!!fieldError}\n className=\"w-full min-w-0 [&>span]:block! [&>span]:min-w-0 [&>span]:truncate\"\n >\n <SelectValue placeholder={t(\"select_an_option\")} />\n </SelectTrigger>\n <SelectContent>\n {selectOptions.map((opt) => (\n <SelectItem\n key={opt.value}\n value={opt.value}\n // Radix Select fires onValueChange only when the value\n // *changes*. Picking the already-selected sentinel\n // would otherwise be a no-op, leaving the user with\n // no way to reopen the address form.\n onClick={\n opt.value === SENTINEL_CREATE_NEW\n ? () => setIsAddressDialogOpen(true)\n : undefined\n }\n >\n {opt.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {fieldError?.message && (\n <p className=\"text-destructive text-sm\">\n {fieldError.message}\n </p>\n )}\n </div>\n )}\n />\n ) : (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setIsAddressDialogOpen(true)}\n disabled={isBusy}\n className=\"w-full\"\n >\n {t(\"add_billing_address\")}\n </Button>\n )}\n\n {selectedAddress && (\n <AddressSummaryCard address={selectedAddress} />\n )}\n </div>\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-x-2\">\n <input\n type=\"checkbox\"\n id=\"set_as_default_cc\"\n checked={setAsDefault}\n onChange={() => setValue(\"set_as_default\", !setAsDefault)}\n disabled={isBusy}\n className=\"size-4\"\n />\n <label htmlFor=\"set_as_default_cc\" className=\"text-sm\">\n {t(\"set_as_default_payment_method\")}\n </label>\n </div>\n\n <Button\n type=\"button\"\n onClick={handleFormSubmit}\n disabled={isBusy}\n >\n {isBusy && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isBusy ? t(\"saving\") : t(\"save_card\")}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {renderAddressFormDialog?.({\n isOpen: isAddressDialogOpen,\n onClose: () => setIsAddressDialogOpen(false),\n onAddressCreated: handleAddressCreated,\n })}\n </>\n );\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\nimport { Lock } from \"lucide-react\";\n\nimport { Dialog, DialogContent, DialogTitle } from \"@fluid-app/ui-primitives\";\n\n/** Display strings for the modal chrome — injected by the host app so the\n * package stays translation-system agnostic. */\nexport interface VgsThreeDSModalLabels {\n title: string;\n cancel: string;\n timedOut: string;\n stillWorking: string;\n dontRefresh: string;\n}\n\nexport interface VgsThreeDSModalProps {\n isOpen: boolean;\n challengeHtml: string;\n challengeUrl?: string;\n onClose: (error?: string) => void;\n labels: VgsThreeDSModalLabels;\n}\n\nfunction escapeAttr(s: string | null): string {\n return (s ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n/**\n * Parse the challenge HTML to extract the form action URL and hidden fields.\n * This lets us POST directly to the bank ACS, bypassing intermediary pages\n * (e.g. Entersekt's red.js auto-submit) that cause extra iframe loads.\n */\nfunction parseDirectPostHtml(\n challengeHtml: string,\n challengeUrl?: string,\n): string | null {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(challengeHtml, \"text/html\");\n const form = doc.querySelector(\"form\");\n if (!form) return null;\n\n const action = challengeUrl || form.getAttribute(\"action\");\n if (!action) return null;\n\n const hiddenInputs = form.querySelectorAll('input[type=\"hidden\"]');\n if (hiddenInputs.length === 0) return null;\n\n const fieldsHtml = Array.from(hiddenInputs)\n .map(\n (input) =>\n `<input type=\"hidden\" name=\"${escapeAttr(input.getAttribute(\"name\"))}\" value=\"${escapeAttr(input.getAttribute(\"value\"))}\">`,\n )\n .join(\"\\n\");\n\n return `<!DOCTYPE html>\n<html>\n<body>\n<form id=\"acsForm\" method=\"POST\" action=\"${escapeAttr(action)}\">\n${fieldsHtml}\n</form>\n<script>document.getElementById(\"acsForm\").submit();</script>\n</body>\n</html>`;\n } catch {\n return null;\n }\n}\n\n/**\n * Pure iframe container for the 3DS challenge.\n *\n * The modal does NOT try to detect challenge completion via load-counting or\n * postMessage — that was fragile across different bank ACS implementations.\n * Instead, `useVgs3DSCompletion` subscribes to the realtime transport as soon\n * as the modal opens and receives the authoritative result from the backend.\n *\n * This modal only handles:\n * - Rendering the ACS challenge iframe\n * - User cancellation (Cancel button in header band)\n * - Hard timeout (6.5 min) as a safety net\n */\nexport function VgsThreeDSModal({\n isOpen,\n challengeHtml,\n challengeUrl,\n onClose,\n labels,\n}: VgsThreeDSModalProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const onCloseRef = useRef(onClose);\n // Capture labels in a ref so the timeout effect can read the current\n // translation without depending on labels in its deps array (host apps\n // typically build the labels object inline per render and would otherwise\n // reset the timer on every re-render).\n const labelsRef = useRef(labels);\n\n useEffect(() => {\n onCloseRef.current = onClose;\n });\n\n useEffect(() => {\n labelsRef.current = labels;\n });\n\n // Build a direct-POST page that skips intermediary auto-redirect pages.\n // Falls back to the original challengeHtml if parsing fails.\n const iframeSrcDoc = useMemo(\n () => parseDirectPostHtml(challengeHtml, challengeUrl) ?? challengeHtml,\n [challengeHtml, challengeUrl],\n );\n\n // Hard timeout — aligned just past the backend safety-net boundary (~6:00)\n // so the cart poll surfaces the backend's verdict (accurate decline,\n // rescued approval, or TIMEOUT) before the modal gives up at 6.5 min.\n // (The old comment attributed the 5-minute value to the EMVCo spec;\n // EMVCo leaves challenge-timeout handling to the ACS/issuer — the real\n // constraint is our own safety-net boundary.) Depends only on isOpen so\n // the timer resets on open, not on content change.\n useEffect(() => {\n if (!isOpen) return;\n\n const timeoutId = setTimeout(() => {\n onCloseRef.current(labelsRef.current.timedOut);\n }, 390_000);\n\n return () => {\n clearTimeout(timeoutId);\n };\n }, [isOpen]);\n\n const handleCancel = () => {\n onClose();\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={handleCancel}>\n {/* z-[110]: a bank challenge must never be occluded by host chrome.\n Card-add surfaces launch this modal from a dialog that pins its\n content at z-[100] (fluid-checkout's credit-card dialog), where the\n default z-50 + DOM-order stacking would paint the host dialog on\n top of the challenge — and Radix would inert it underneath. */}\n <DialogContent\n className=\"z-[110] h-[600px] max-w-2xl p-0 sm:max-w-2xl\"\n overlayClassName=\"z-[110]\"\n showCloseButton={false}\n >\n <div className=\"flex h-full w-full flex-col\">\n {/* Header band — replaces VisuallyHidden DialogTitle */}\n <div className=\"flex items-center justify-between border-b border-gray-200 px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <Lock className=\"h-4 w-4 text-gray-600\" aria-hidden=\"true\" />\n <DialogTitle className=\"text-sm font-semibold text-gray-900\">\n {labels.title}\n </DialogTitle>\n </div>\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded text-xs text-indigo-600 hover:text-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none\"\n >\n {labels.cancel}\n </button>\n </div>\n\n {/* Iframe (unchanged behavior). */}\n <div className=\"relative flex-1\">\n {challengeHtml && (\n <iframe\n ref={iframeRef}\n srcDoc={iframeSrcDoc}\n className=\"h-full w-full border-0\"\n title=\"VGS 3DS Challenge\"\n sandbox=\"allow-same-origin allow-scripts allow-forms allow-top-navigation allow-popups\"\n />\n )}\n </div>\n\n {/* Footer band — indeterminate progress + status caption + tab-refresh hint */}\n <div className=\"border-t border-gray-200 px-4 py-3\">\n <div\n data-testid=\"threeds-progress-bar\"\n role=\"progressbar\"\n aria-label={labels.stillWorking}\n className=\"relative h-1 overflow-hidden rounded-full bg-indigo-100\"\n >\n <div className=\"threeds-progress-pill absolute top-0 h-full w-[30%] rounded-full bg-indigo-500\" />\n </div>\n <div className=\"mt-2 flex items-center justify-between text-xs\">\n <span className=\"text-gray-600\">{labels.stillWorking}</span>\n <span className=\"text-gray-400\">{labels.dontRefresh}</span>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport default VgsThreeDSModal;\n","// Pure attempt-scoping predicate shared by the poll and Pusher paths of\n// useVgs3DSCompletion. Presence-guarded on BOTH sides: when either the\n// active attempt key or the result's key is missing (backend not yet\n// deployed, old event shape), the result is honored — exactly today's\n// unscoped behavior.\nexport function belongsToActiveAttempt(\n activeMerchantTxId: string | undefined,\n resultMerchantTxId: string | undefined,\n): boolean {\n if (!activeMerchantTxId || !resultMerchantTxId) return true;\n return resultMerchantTxId === activeMerchantTxId;\n}\n","import { useEffect, useEffectEvent, useRef } from \"react\";\nimport type { ThreeDSStatus } from \"@fluid-app/fluid-pay-core/types\";\nimport { belongsToActiveAttempt } from \"./three-ds-attempt\";\n\nexport interface Vgs3DSCompletionResult {\n success: boolean;\n transactionId: string;\n status?: string;\n}\n\nexport interface Vgs3DSChallengeEvent {\n xid: string;\n /** Optional for backward compatibility: older events may omit it, and the\n * attempt-scoping guard deliberately honors key-less events. */\n merchant_transaction_id?: string;\n status: \"success\" | \"failed\";\n challenge_status?: string;\n error_message?: string;\n}\n\n/** Minimal structural slice of a realtime channel — pusher-js's `Channel`\n * satisfies this as-is (its `bind`/`unbind` return `Channel`, which is\n * assignable to a `void`-returning method). */\nexport interface ThreeDsRealtimeChannel {\n bind(eventName: string, handler: (data: Vgs3DSChallengeEvent) => void): void;\n unbind(\n eventName: string,\n handler: (data: Vgs3DSChallengeEvent) => void,\n ): void;\n}\n\n/** Transport abstraction for the fast path — the app's `pusherClient`\n * satisfies this structurally with no adapter. */\nexport interface ThreeDsRealtimeTransport {\n subscribe(channelName: string): ThreeDsRealtimeChannel | null;\n unsubscribe(channelName: string): void;\n}\n\ninterface UseVgs3DSCompletionOptions {\n /** Realtime channel carrying the `vgs_3ds_challenge` event\n * (e.g. `private-cart-{cartToken}`). */\n channelName: string;\n transport: ThreeDsRealtimeTransport;\n /** Status poll — the reliable fallback alongside the realtime fast path. */\n checkStatus: () => Promise<ThreeDSStatus | null>;\n onComplete: (result: Vgs3DSCompletionResult) => void;\n onError: (error: string) => void;\n /** Subscribe to the realtime channel and start polling when true (i.e. when the 3DS modal is open). */\n enabled?: boolean;\n /** The active attempt's merchant_tx_id — when present, completion signals\n * for any OTHER attempt are ignored (stale metadata, late webhooks). */\n merchantTxId?: string;\n /** Called when the attempt was canceled (e.g. from another tab) — a quiet,\n * no-error terminal. */\n onCanceled?: () => void;\n}\n\n// Poll interval and timeout for the status-poll fallback.\n// Polling at 3-second intervals is the reliable secondary check alongside\n// the Pusher fast path (the backend broadcasts the result immediately).\n// The 6.5-minute timeout sits past the backend safety-net boundary (~6:00),\n// so the poll surfaces the backend's verdict — accurate decline, rescued\n// approval, or TIMEOUT — instead of the frontend self-declaring first.\nconst POLL_INTERVAL_MS = 3_000;\nconst TIMEOUT_MS = 390_000;\n\n/**\n * Subscribes to the realtime transport and polls the injected status check\n * for the 3DS challenge result.\n *\n * Activated by `enabled` — pass `vgsThreeDSModal.isOpen` so the subscription\n * starts as soon as the challenge modal opens. No URL-param dance needed.\n */\nexport function useVgs3DSCompletion({\n channelName,\n transport,\n checkStatus,\n onComplete,\n onError,\n enabled = false,\n merchantTxId,\n onCanceled,\n}: UseVgs3DSCompletionOptions) {\n const hasHandledRef = useRef(false);\n const isSubscribedRef = useRef(false);\n\n // Kept current via a per-render effect so an inline `transport` value at\n // the call site never retriggers the main effect.\n const transportRef = useRef(transport);\n useEffect(() => {\n transportRef.current = transport;\n });\n\n const handleComplete = useEffectEvent((result: Vgs3DSCompletionResult) => {\n onComplete(result);\n });\n\n const handleError = useEffectEvent((error: string) => {\n onError(error);\n });\n\n const handleCanceled = useEffectEvent(() => {\n onCanceled?.();\n });\n\n // Non-reactive wrapper so an inline `checkStatus` closure at the call site\n // never retriggers the main effect. Called from the poll during the active\n // phase only — never from cleanup.\n const performStatusCheck = useEffectEvent(() => checkStatus());\n\n // Shared handler for Pusher events, polling results, and timeout.\n // hasHandledRef ensures only the first source to resolve wins.\n const handleResult = useEffectEvent(\n (result: {\n status: \"success\" | \"failed\";\n errorMessage?: string;\n transactionId?: string;\n }) => {\n if (hasHandledRef.current) return;\n hasHandledRef.current = true;\n\n if (result.status === \"success\") {\n handleComplete({\n success: true,\n transactionId: result.transactionId ?? \"\",\n status: result.status,\n });\n } else {\n handleError(result.errorMessage || \"3DS verification failed\");\n }\n },\n );\n\n // Reset handled flag when enabled toggles off (modal closed / new attempt).\n useEffect(() => {\n if (!enabled && hasHandledRef.current) {\n hasHandledRef.current = false;\n }\n }, [enabled]);\n\n useEffect(() => {\n if (!enabled || !channelName || hasHandledRef.current) {\n return;\n }\n\n if (isSubscribedRef.current) {\n return;\n }\n\n let cancelled = false;\n // Captured ONCE so subscribe and the cleanup's unsubscribe use the same\n // transport object even if a different one is passed mid-flight.\n const activeTransport = transportRef.current;\n\n isSubscribedRef.current = true;\n const channel = activeTransport.subscribe(channelName);\n const hasPusher = !!channel;\n\n if (!hasPusher) {\n console.error(\n \"[VGS 3DS] Pusher subscribe returned null — using status polling only\",\n );\n isSubscribedRef.current = false;\n // intentionally NOT returning — polling + timeout below are the fallback\n }\n\n // --- Fast path: Pusher event ---\n const handleVgs3DSChallenge = (data: Vgs3DSChallengeEvent) => {\n if (cancelled) return;\n\n // Attempt-scoping: ignore events for a different (older) attempt.\n if (!belongsToActiveAttempt(merchantTxId, data.merchant_transaction_id)) {\n return;\n }\n\n handleResult({\n status: data.status,\n errorMessage: data.error_message,\n transactionId: data.merchant_transaction_id,\n });\n };\n\n if (hasPusher) {\n channel.bind(\"vgs_3ds_challenge\", handleVgs3DSChallenge);\n }\n\n // --- Reliable fallback: poll the injected status check ---\n const pollInterval = setInterval(async () => {\n if (hasHandledRef.current || cancelled) return;\n\n const status = await performStatusCheck();\n if (cancelled || !status) return;\n\n // Attempt-scoping: stale metadata from a previous attempt must not\n // resolve this modal (the backend wipes per-attempt keys on re-init,\n // but a late webhook can still land mid-retry).\n if (!belongsToActiveAttempt(merchantTxId, status.merchantTxId)) {\n return;\n }\n\n if (status.completed) {\n handleResult({ status: \"success\" });\n } else if (status.failed) {\n handleResult({\n status: \"failed\",\n errorMessage:\n status.errorMessage ||\n `3DS verification ${status.status || \"failed\"}`,\n });\n } else if (status.canceled) {\n // Canceled (possibly from another tab): quiet no-error terminal.\n if (!hasHandledRef.current) {\n hasHandledRef.current = true;\n handleCanceled();\n }\n }\n }, POLL_INTERVAL_MS);\n\n // --- Safety net: hard timeout ---\n const timeout = setTimeout(() => {\n if (hasHandledRef.current || cancelled) return;\n\n console.error(\"[VGS 3DS] Timed out waiting for 3DS result\");\n handleResult({\n status: \"failed\",\n errorMessage: \"Payment verification timed out. Please try again.\",\n });\n }, TIMEOUT_MS);\n\n // Cleanup — cancelled flag prevents in-flight polls from calling handleResult\n return () => {\n cancelled = true;\n clearTimeout(timeout);\n clearInterval(pollInterval);\n if (hasPusher) {\n channel.unbind(\"vgs_3ds_challenge\", handleVgs3DSChallenge);\n activeTransport.unsubscribe(channelName);\n }\n isSubscribedRef.current = false;\n };\n }, [enabled, channelName, merchantTxId]);\n}\n","import { z } from \"zod\";\n\nexport const browserInfoSchema = z.object({\n color_depth: z.number().optional(),\n screen_height: z.number().optional(),\n screen_width: z.number().optional(),\n time_zone: z.number().optional(),\n java_enabled: z.boolean().optional(),\n java_script_enabled: z.boolean().optional(),\n language: z.string().optional(),\n user_agent: z.string().optional(),\n accept_header: z.string().optional(),\n});\n\nexport type BrowserInfo = z.infer<typeof browserInfoSchema>;\n\nexport function getBrowserInfo(): BrowserInfo | undefined {\n // Check if we're in a browser environment\n if (typeof window === \"undefined\") {\n console.warn(\"getBrowserInfo called in non-browser environment\");\n return undefined;\n }\n\n try {\n const { screen, navigator } = window;\n\n // Validate that we have the required objects\n if (!screen || !navigator) {\n console.warn(\"Missing screen or navigator objects\");\n return undefined;\n }\n\n const browserInfo: BrowserInfo = {\n color_depth: screen.colorDepth || undefined,\n screen_height: screen.height || undefined,\n screen_width: screen.width || undefined,\n time_zone: new Date().getTimezoneOffset(),\n java_enabled:\n typeof navigator.javaEnabled === \"function\"\n ? navigator.javaEnabled()\n : false,\n java_script_enabled: true,\n language: navigator.language || undefined,\n user_agent: navigator.userAgent || undefined,\n accept_header: \"*/*\",\n };\n\n // Validate that we have at least some essential info\n if (\n !browserInfo.screen_height ||\n !browserInfo.screen_width ||\n !browserInfo.user_agent\n ) {\n console.warn(\"Missing essential browser info fields\");\n }\n\n return browserInfo;\n } catch (error) {\n console.error(\"Error getting browser info:\", error);\n return undefined;\n }\n}\n","/**\n * TypeScript interfaces for portal-tenant pay management.\n * These types mirror the portal-tenant BFF `/api/pay/*` endpoints.\n * Packages import from here instead of from any concrete API client.\n */\n\nimport type { ResponseMeta } from \"./fluidos-api\";\n\n// ---------------------------------------------------------------------------\n// Address\n// ---------------------------------------------------------------------------\n\nexport interface PayAddress {\n id: number;\n name: string | null;\n street1: string;\n street2: string | null;\n city: string;\n state: string;\n zip: string;\n country: string;\n phone?: string | null;\n default: boolean;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface CreateAddressBody {\n address: {\n name?: string | null;\n street1: string;\n street2?: string | null;\n city: string;\n state: string;\n zip: string;\n country: string;\n phone?: string | null;\n default?: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Payment method\n// ---------------------------------------------------------------------------\n\nexport interface PayPaymentMethod {\n id: number;\n type: string;\n brand: string | null;\n last_four: string;\n logo_url: string | null;\n exp_month: number | null;\n exp_year: number | null;\n card_holder: string | null;\n default: boolean;\n billing_address: PayAddress | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport const TOKEN_PROVIDER_VGS = \"vgs\" as const;\n\n// Body for POST /api/pay/payment_methods on the portal-tenant BFF.\n//\n// Note: the BFF always creates the PM with customer scope\n// (PaymentMethodCreator hardcodes set_customer_payment_method: true on its\n// way into the FluidPay action), so PMs created via this body are always\n// immediately referenceable from customer.payment_methods — no need to\n// expose set_customer_payment_method to the client. The checkout app's\n// direct /fluid_pay/create_payment_method route exposes it because that\n// route does not have the same default.\nexport interface CreatePaymentMethodBody {\n payment_method: {\n type: \"card\";\n token: string;\n token_provider?: string;\n exp_month: string;\n exp_year: string;\n cvv_token?: string;\n card_holder?: string;\n default?: boolean;\n bin?: string;\n last_four?: string;\n card_network?: string;\n };\n billing_address_id: number;\n // `| null` mirrors the OpenAPI contract / generated client so callers can send\n // an explicit null instead of being forced to omit the field.\n browser_info?: Record<string, unknown> | null;\n}\n\nexport interface UpdatePaymentMethodBody {\n payment_method: {\n default?: boolean;\n billing_address?: {\n name?: string | null;\n street1: string;\n street2?: string | null;\n city: string;\n state: string;\n zip: string;\n country: string;\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Vault\n// ---------------------------------------------------------------------------\n\nexport interface VaultCredentials {\n vault_id: string;\n environment: string;\n route_id: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Response types\n// ---------------------------------------------------------------------------\n\nexport interface AddressesResponse {\n addresses: PayAddress[];\n meta: ResponseMeta;\n}\n\nexport interface PaymentMethodsResponse {\n payment_methods: PayPaymentMethod[];\n meta: ResponseMeta;\n}\n\nexport interface CreatedPaymentMethodResult {\n payment_method: PayPaymentMethod;\n meta: ResponseMeta;\n}\n\nexport interface ThreeDsRequiredCreateResult {\n three_ds: {\n status: \"three_ds_required\";\n session_token: string;\n tds_method_content: string | null;\n fingerprinting_required: boolean | null;\n merchant_tx_id: string | null;\n };\n meta: ResponseMeta;\n}\n\n// Discriminated union: 201 (created PM) | 200 (3DS required).\nexport type CreatePaymentMethodResponse =\n | CreatedPaymentMethodResult\n | ThreeDsRequiredCreateResult;\n\nexport function isThreeDsRequiredCreate(\n r: CreatePaymentMethodResponse,\n): r is ThreeDsRequiredCreateResult {\n return \"three_ds\" in r && r.three_ds.status === \"three_ds_required\";\n}\n\nexport interface ConfirmApprovedResult {\n status: \"approved\";\n session_token: string;\n payment_method: PayPaymentMethod;\n was_created?: boolean;\n}\n\nexport interface ConfirmChallengeResult {\n status: \"three_ds_challenge\";\n session_token: string;\n challenge_form: string | null;\n challenge_url: string | null;\n}\n\nexport type ConfirmPaymentMethod3DSResponse =\n | ConfirmApprovedResult\n | ConfirmChallengeResult;\n\n// 3DS session lifecycle state from the BFF. The known values are listed for\n// autocomplete + typo-safety, but the type stays open (`string & {}`) because\n// the backend enum may grow: the adapter forwards the wire string without a\n// lying `as` cast, and an unrecognized state stays non-terminal in\n// mapSessionStateToThreeDSStatus — resolved by the EMVCo polling timeout rather\n// than mis-mapped to completed/failed/canceled.\nexport type ThreeDsSessionState =\n | \"initialized\"\n | \"pending\"\n | \"approved\"\n | \"denied\"\n | \"timed_out\"\n | \"cancelled\"\n | \"skipped\"\n | (string & {});\n\nexport interface ThreeDsStatusResponse {\n session_token: string;\n state: ThreeDsSessionState;\n payment_method_id: number | null;\n expired: boolean | null;\n}\n\nexport interface ThreeDsCancelResponse {\n session_token: string;\n state: ThreeDsSessionState;\n cancelled: boolean | null;\n}\n\nexport interface VaultResponse {\n vault: VaultCredentials;\n meta: ResponseMeta;\n}\n\n// ---------------------------------------------------------------------------\n// Points ledger\n// ---------------------------------------------------------------------------\n\nexport interface PointsLedgerEntry {\n id: number;\n amount: number;\n total_balance: number;\n metadata: Record<string, unknown> | null;\n created_at: string;\n}\n\nexport interface PointsLedgersResponse {\n points_ledgers: PointsLedgerEntry[];\n meta: ResponseMeta;\n}\n","import { type FluidPayApi, type fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport type { PayApi } from \"@fluid-app/portal-core/pay-api\";\nimport type { PayPaymentMethod } from \"@fluid-app/portal-core/pay-types\";\n\n/**\n * Maps a portal-tenant PayPaymentMethod to the fluidPay shape consumed by\n * profile-ui components (PaymentMethodDropdown, CreditCardFormDialog, etc.).\n * Co-located with the adapter because both speak the portal-tenant ↔ fluid-pay\n * boundary.\n */\nexport function mapToFluidPayPaymentMethod(\n raw: PayPaymentMethod,\n): fluidPay.CustomerPaymentMethod {\n return {\n id: raw.id,\n created_at: raw.created_at ?? \"\",\n default: raw.default,\n details: {\n card_network: raw.brand,\n card_brand: raw.brand,\n card_holder: raw.card_holder,\n last_four: raw.last_four,\n logo_url: raw.logo_url ?? undefined,\n exp_month: raw.exp_month ?? undefined,\n exp_year: raw.exp_year ?? undefined,\n },\n billing_address: raw.billing_address\n ? {\n name: raw.billing_address.name,\n address1: raw.billing_address.street1,\n address2: raw.billing_address.street2,\n city: raw.billing_address.city,\n state: raw.billing_address.state,\n zip: raw.billing_address.zip,\n country_code: raw.billing_address.country,\n }\n : null,\n payment_type: raw.type,\n source: \"\",\n updated_at: raw.updated_at ?? \"\",\n };\n}\n\nconst notImplemented = () => {\n throw new Error(\"Not available in portal-tenant context\");\n};\n\n/**\n * Adapts the portal-tenant PayApi to the FluidPayApi shape required by\n * FluidPayCoreProvider. Only `vault.fetchCredentials` is implemented —\n * the rest of the FluidPayApi surface is not used by the profile-ui\n * components that consume this provider in the portal context.\n */\nexport function createFluidPayApiAdapter(payApi: PayApi): FluidPayApi {\n return {\n customers: { fetchAccount: notImplemented, update: notImplemented },\n addresses: {\n fetchAll: notImplemented,\n create: notImplemented,\n update: notImplemented,\n delete: notImplemented,\n },\n paymentMethods: {\n fetchAll: notImplemented,\n addCreditCard: notImplemented,\n delete: notImplemented,\n updateDefault: notImplemented,\n update: notImplemented,\n },\n vault: {\n fetchCredentials: async () => {\n const response = await payApi.fetchVaultCredentials();\n return {\n vault: {\n vault_id: response.vault.vault_id,\n environment: response.vault.environment,\n route_id: response.vault.route_id ?? \"\",\n requires_3ds: false,\n },\n meta: {\n request_id: response.meta?.request_id ?? \"\",\n timestamp: response.meta?.timestamp ?? \"\",\n },\n } satisfies fluidPay.VaultResponse;\n },\n },\n };\n}\n","export interface StateOption {\n readonly isoCode: string;\n readonly name: string;\n}\n\ninterface AdministrativeAreaDetails {\n readonly state?: string;\n readonly administrativeAreaShort?: string;\n readonly administrativeAreaLong?: string;\n}\n\nfunction normalize(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction firstPresent(...values: Array<string | undefined>): string {\n return values.find((value) => value && value.trim() !== \"\") ?? \"\";\n}\n\nfunction findStateByIsoCode(\n stateOptions: readonly StateOption[],\n value: string,\n): StateOption | undefined {\n const normalizedValue = normalize(value);\n if (!normalizedValue) return undefined;\n return stateOptions.find(\n (state) => normalize(state.isoCode) === normalizedValue,\n );\n}\n\nfunction findStateByName(\n stateOptions: readonly StateOption[],\n value: string,\n): StateOption | undefined {\n const normalizedValue = normalize(value);\n if (!normalizedValue) return undefined;\n return stateOptions.find(\n (state) => normalize(state.name) === normalizedValue,\n );\n}\n\nexport function resolveAdministrativeAreaState(\n details: AdministrativeAreaDetails,\n stateOptions: readonly StateOption[] = [],\n): string {\n const displayValue = firstPresent(\n details.state,\n details.administrativeAreaLong,\n details.administrativeAreaShort,\n );\n\n if (stateOptions.length === 0) return displayValue;\n\n const candidates = [\n details.administrativeAreaShort,\n details.state,\n details.administrativeAreaLong,\n ].filter((value): value is string => Boolean(value));\n\n for (const candidate of candidates) {\n const state =\n findStateByIsoCode(stateOptions, candidate) ??\n findStateByName(stateOptions, candidate);\n if (state) return state.isoCode;\n }\n\n return displayValue;\n}\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type JSX,\n} from \"react\";\nimport {\n useController,\n type Control,\n type FieldValues,\n type Path,\n type PathValue,\n type UseFormSetValue,\n} from \"react-hook-form\";\nimport { Input, Label } from \"@fluid-app/ui-primitives\";\nimport { useAddressAutocompleteApi } from \"../api-context\";\nimport type { AddressDetails, AddressSuggestion } from \"../api\";\nimport {\n resolveAdministrativeAreaState,\n type StateOption,\n} from \"../administrative-area\";\n\nexport interface AddressAutocompleteInputProps<T extends FieldValues> {\n readonly control: Control<T>;\n readonly setValue: UseFormSetValue<T>;\n readonly countryIso: string;\n readonly addressLineField: Path<T>;\n readonly cityField: Path<T>;\n readonly stateField: Path<T>;\n readonly postalCodeField: Path<T>;\n readonly stateOptions?: readonly StateOption[];\n readonly id?: string;\n readonly label?: string;\n readonly placeholder?: string;\n readonly disabled?: boolean;\n}\n\nconst SET_OPTS = {\n shouldValidate: true,\n shouldDirty: true,\n shouldTouch: true,\n} as const;\n\nconst DEBOUNCE_MS = 250;\nconst EMPTY_STATE_OPTIONS: readonly StateOption[] = [];\n\nexport function AddressAutocompleteInput<T extends FieldValues>({\n control,\n setValue,\n countryIso,\n addressLineField,\n cityField,\n stateField,\n postalCodeField,\n stateOptions = EMPTY_STATE_OPTIONS,\n id,\n label,\n placeholder,\n disabled,\n}: AddressAutocompleteInputProps<T>): JSX.Element {\n const api = useAddressAutocompleteApi();\n const {\n field,\n fieldState: { error },\n } = useController({ name: addressLineField, control });\n const [suggestions, setSuggestions] = useState<readonly AddressSuggestion[]>(\n [],\n );\n const [isOpen, setIsOpen] = useState(false);\n const [needsCountry, setNeedsCountry] = useState(false);\n const containerRef = useRef<HTMLDivElement | null>(null);\n // Monotonically increasing request ID. We only apply a fetch result when its\n // ID still matches the latest issued one — guards against stale responses\n // arriving out of order under network jitter.\n const requestIdRef = useRef(0);\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isCountrySelected = useMemo(\n () => Boolean(countryIso && countryIso.trim() !== \"\"),\n [countryIso],\n );\n const inputId = id ?? addressLineField;\n\n useEffect(() => {\n function onClickOutside(e: MouseEvent) {\n if (\n containerRef.current &&\n !containerRef.current.contains(e.target as Node)\n ) {\n setIsOpen(false);\n setNeedsCountry(false);\n }\n }\n document.addEventListener(\"mousedown\", onClickOutside);\n return () => document.removeEventListener(\"mousedown\", onClickOutside);\n }, []);\n\n useEffect(\n () => () => {\n if (debounceTimerRef.current !== null) {\n clearTimeout(debounceTimerRef.current);\n }\n },\n [],\n );\n\n const populate = useCallback(\n (s: AddressDetails) => {\n setValue(\n addressLineField,\n s.streetLine as PathValue<T, Path<T>>,\n SET_OPTS,\n );\n setValue(cityField, s.city as PathValue<T, Path<T>>, SET_OPTS);\n setValue(\n stateField,\n resolveAdministrativeAreaState(s, stateOptions) as PathValue<\n T,\n Path<T>\n >,\n SET_OPTS,\n );\n setValue(\n postalCodeField,\n s.postalCode as PathValue<T, Path<T>>,\n SET_OPTS,\n );\n },\n [\n addressLineField,\n cityField,\n postalCodeField,\n setValue,\n stateField,\n stateOptions,\n ],\n );\n\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n field.onChange(value);\n\n // Cancel any pending fetch from a prior keystroke.\n if (debounceTimerRef.current !== null) {\n clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = null;\n }\n\n if (value.length < 1) {\n setIsOpen(false);\n setNeedsCountry(false);\n setSuggestions([]);\n return;\n }\n if (!isCountrySelected) {\n setNeedsCountry(true);\n setIsOpen(true);\n return;\n }\n setNeedsCountry(false);\n\n const requestId = ++requestIdRef.current;\n debounceTimerRef.current = setTimeout(() => {\n debounceTimerRef.current = null;\n void api.fetchSuggestions({ query: value, countryIso }).then((next) => {\n // Drop stale responses — a newer keystroke has already issued a\n // request, and applying this would clobber its result.\n if (requestId !== requestIdRef.current) return;\n setSuggestions(next);\n setIsOpen(true);\n });\n }, DEBOUNCE_MS);\n },\n [api, countryIso, field, isCountrySelected],\n );\n\n const handleClick = useCallback(\n async (suggestion: AddressSuggestion) => {\n // Cancel any debounced fetch still pending from a recent keystroke and\n // advance the request ID so any in-flight response is dropped — without\n // this, the timer could fire after a selection and re-open the dropdown.\n if (debounceTimerRef.current !== null) {\n clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = null;\n }\n const clickId = ++requestIdRef.current;\n\n if (!suggestion.needsDetailLookup) {\n populate({\n streetLine: suggestion.streetLine ?? suggestion.text,\n city: suggestion.city ?? \"\",\n state: suggestion.state ?? \"\",\n postalCode: suggestion.postalCode ?? \"\",\n });\n setIsOpen(false);\n return;\n }\n const { details, drilldownSuggestions } = await api.fetchDetails({\n suggestion,\n countryIso,\n });\n // Drop the result if the user typed during the network round-trip — a\n // newer keystroke has superseded this click and applying its result\n // would clobber what the user has since entered.\n if (clickId !== requestIdRef.current) return;\n if (details) {\n populate(details);\n setIsOpen(false);\n return;\n }\n setSuggestions(drilldownSuggestions);\n setIsOpen(true);\n },\n [api, countryIso, populate],\n );\n\n return (\n <div className=\"space-y-1\">\n {label && (\n <Label htmlFor={inputId} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <div ref={containerRef} className=\"relative\">\n <Input\n id={inputId}\n type=\"text\"\n ref={field.ref}\n value={(field.value as string) ?? \"\"}\n onChange={handleChange}\n onBlur={field.onBlur}\n placeholder={placeholder}\n disabled={disabled}\n aria-invalid={!!error}\n />\n {isOpen && needsCountry && (\n <div className=\"bg-muted text-foreground absolute z-50 mt-1 w-full rounded-md px-3 py-2 text-sm shadow-lg\">\n Please select a country first to enable address suggestions\n </div>\n )}\n {isOpen && !needsCountry && suggestions.length > 0 && (\n <div className=\"bg-background text-foreground border-border absolute z-50 mt-1 max-h-60 w-full overflow-y-auto rounded-md border shadow-lg\">\n {suggestions.map((s) => (\n <button\n key={s.id}\n type=\"button\"\n onClick={() => {\n void handleClick(s).catch((err) => {\n // handleClick is async because of fetchDetails — the current\n // Smarty adapter and NULL_API swallow errors, so this is\n // belt-and-suspenders for future adapters that might throw.\n console.error(\"Address autocomplete click failed:\", err);\n });\n }}\n className=\"hover:bg-muted block w-full cursor-pointer px-3 py-2 text-left text-sm\"\n >\n {s.text}\n {s.needsDetailLookup ? \" →\" : \"\"}\n </button>\n ))}\n </div>\n )}\n </div>\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </div>\n );\n}\n","// Helper to render the device fingerprinting iframe.\n//\n// Browser-only: the function body requires `document` — never call during\n// SSR. Importing this module is SSR-safe; only invocation touches the DOM.\nexport const renderFingerprintingIframe = (html: string): Promise<void> => {\n return new Promise((resolve) => {\n const iframe = document.createElement(\"iframe\");\n iframe.srcdoc = html;\n iframe.style.display = \"none\";\n\n document.body.appendChild(iframe);\n\n // Wait for fingerprinting to complete (usually <10 seconds)\n setTimeout(() => {\n document.body.removeChild(iframe);\n resolve();\n }, 10000);\n });\n};\n","import type { ThreeDsRealtimeTransport } from \"@fluid-app/fluid-pay-ui\";\nimport type { ThreeDSStatus } from \"@fluid-app/fluid-pay-core/types\";\nimport type { ThreeDsSessionState } from \"@fluid-app/portal-core/pay-types\";\n\n/**\n * Polling-only realtime transport. A no-op channel keeps useVgs3DSCompletion's\n * `hasPusher` true so it does not log the misleading \"subscribe returned null\"\n * error; the 3s status poll is the sole resolver. The Pusher fast-follow\n * replaces this constant with a real pusher-js client.\n */\nexport const POLLING_ONLY_TRANSPORT: ThreeDsRealtimeTransport = {\n subscribe: () => ({ bind: () => {}, unbind: () => {} }),\n unsubscribe: () => {},\n};\n\n/**\n * Map the BFF 3ds_status session state -> the FE-A hook's ThreeDSStatus. Only\n * the known terminal states resolve the poll (approved/skipped -> completed,\n * denied/timed_out/expired -> failed, cancelled -> canceled); any other value\n * (initialized/pending or a future backend state) stays non-terminal so the\n * poll keeps running until the EMVCo timeout, rather than being mis-classified.\n */\nexport function mapSessionStateToThreeDSStatus(\n state: ThreeDsSessionState,\n expired: boolean | null,\n): ThreeDSStatus {\n return {\n completed: state === \"approved\" || state === \"skipped\",\n failed: state === \"denied\" || state === \"timed_out\" || expired === true,\n canceled: state === \"cancelled\",\n status: state,\n };\n}\n\n/**\n * Dig the portal BFF error code off a thrown ApiError. Structural and\n * self-validating: a non-ApiError simply yields undefined (no instanceof gate).\n */\nexport function cardAdd3DSErrorCode(error: unknown): string | undefined {\n const data = (error as { data?: unknown } | null)?.data;\n const meta = (data as { meta?: unknown } | null)?.meta;\n const code = (meta as { error_code?: unknown } | null)?.error_code;\n return typeof code === \"string\" ? code : undefined;\n}\n\n/**\n * Customer-facing message off a thrown portal ApiError. The client lifts the\n * portal `{ error: { message } }` onto ApiError.message; fall back to the\n * nested field, then the supplied generic. (`data.error_message` is the\n * storefront shape and is always undefined here.)\n */\nexport function cardAdd3DSErrorMessage(\n error: unknown,\n fallback: string,\n): string {\n if (error instanceof Error && error.message) return error.message;\n const data = (error as { data?: unknown } | null)?.data;\n const nested = (data as { error?: { message?: unknown } } | null)?.error\n ?.message;\n return typeof nested === \"string\" ? nested : fallback;\n}\n","import { useEffectEvent, useRef, useState, type ReactNode } from \"react\";\nimport { renderFingerprintingIframe } from \"@fluid-app/fluid-pay-core/utils/fingerprinting\";\nimport type { ThreeDSStatus } from \"@fluid-app/fluid-pay-core/types\";\nimport { useVgs3DSCompletion, VgsThreeDSModal } from \"@fluid-app/fluid-pay-ui\";\nimport type { PayApi } from \"@fluid-app/portal-core/pay-api\";\nimport type {\n ConfirmApprovedResult,\n ThreeDsRequiredCreateResult,\n} from \"@fluid-app/portal-core/pay-types\";\nimport {\n POLLING_ONLY_TRANSPORT,\n cardAdd3DSErrorCode,\n cardAdd3DSErrorMessage,\n mapSessionStateToThreeDSStatus,\n} from \"./three-ds-card-add-helpers\";\n\n// Wait before the single confirm retry when a completion signal races the\n// backend's upstream reconcile (422 three_ds_pending).\nconst PENDING_RETRY_DELAY_MS = 2_000;\nconst waitFor = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\ninterface ActiveSession {\n sessionToken: string;\n merchantTxId: string | undefined;\n}\ninterface PendingChallenge {\n html: string;\n url?: string;\n}\n\nexport interface UsePortalCardAdd3DSFlowOptions {\n payApi: PayApi;\n onApproved: (approved: ConfirmApprovedResult) => void | Promise<void>;\n onFailed: (message: string) => void;\n onCanceled: () => void;\n t: (key: string) => string;\n}\n\nexport interface PortalCardAdd3DSFlow {\n begin: (created: ThreeDsRequiredCreateResult) => Promise<void>;\n modal: ReactNode;\n isActive: boolean;\n}\n\nexport function usePortalCardAdd3DSFlow({\n payApi,\n onApproved,\n onFailed,\n onCanceled,\n t,\n}: UsePortalCardAdd3DSFlowOptions): PortalCardAdd3DSFlow {\n const [session, setSession] = useState<ActiveSession | null>(null);\n const [challenge, setChallenge] = useState<PendingChallenge | null>(null);\n // Synchronous mirror for the single-flight guard and async continuations.\n const activeSessionRef = useRef<ActiveSession | null>(null);\n\n // Always call the latest surface callbacks.\n const emitApproved = useEffectEvent((approved: ConfirmApprovedResult) =>\n onApproved(approved),\n );\n const emitFailed = useEffectEvent((message: string) => onFailed(message));\n const emitCanceled = useEffectEvent(() => onCanceled());\n\n const resetFlow = () => {\n activeSessionRef.current = null;\n setSession(null);\n setChallenge(null);\n };\n\n // Exactly-one-terminal guard: each finish* no-ops once a terminal has won\n // (resetFlow nulls the ref right before the emit). Covers the modal's\n // Cancel/timeout and begin()'s catch racing a terminal.\n const finishApproved = async (approved: ConfirmApprovedResult) => {\n if (!activeSessionRef.current) return;\n resetFlow();\n await emitApproved(approved);\n };\n const finishFailed = (message: string) => {\n if (!activeSessionRef.current) return;\n resetFlow();\n emitFailed(message);\n };\n const finishCanceled = () => {\n if (!activeSessionRef.current) return;\n resetFlow();\n emitCanceled();\n };\n\n const failureMessageFrom = (error: unknown): string => {\n if (cardAdd3DSErrorCode(error) === \"three_ds_session_not_found\") {\n return t(\"payment_verification_timed_out\");\n }\n return cardAdd3DSErrorMessage(\n error,\n t(\"payment_verification_didnt_complete\"),\n );\n };\n\n const begin = async (created: ThreeDsRequiredCreateResult): Promise<void> => {\n // Single-flight: one bank check at a time.\n if (activeSessionRef.current) return;\n\n const {\n session_token,\n merchant_tx_id,\n fingerprinting_required,\n tds_method_content,\n } = created.three_ds;\n // No attempt activated yet — emit directly (the finish* ref-guard would swallow this).\n if (!session_token) {\n emitFailed(t(\"payment_verification_didnt_complete\"));\n return;\n }\n\n const active: ActiveSession = {\n sessionToken: session_token,\n merchantTxId: merchant_tx_id ?? undefined,\n };\n activeSessionRef.current = active;\n setSession(active);\n\n try {\n // Inside the try so a synchronous throw lands on finishFailed — an\n // escape would leave the ref set and deadlock single-flight.\n if (\n fingerprinting_required !== false &&\n typeof tds_method_content === \"string\" &&\n tds_method_content.length > 0\n ) {\n await renderFingerprintingIframe(tds_method_content);\n }\n\n const confirmation = await payApi.confirmPaymentMethod3DS({\n session_token: active.sessionToken,\n });\n if (confirmation.status === \"approved\") {\n await finishApproved(confirmation);\n return;\n }\n // A challenge with no form renders an empty modal that strands the user\n // — fail fast. (challenge_url alone is only the POST target for the\n // form's hidden fields; the modal renders the iframe from the form HTML.)\n if (!confirmation.challenge_form) {\n finishFailed(t(\"payment_verification_didnt_complete\"));\n return;\n }\n setChallenge({\n html: confirmation.challenge_form,\n url: confirmation.challenge_url ?? undefined,\n });\n } catch (error) {\n finishFailed(failureMessageFrom(error));\n }\n };\n\n // Replays confirm after a completion signal. The modal stays open (its\n // Cancel/timeout terminal can fire mid-await), so check staleness against\n // the attempt captured at entry after every await — land on EXACTLY ONE\n // terminal.\n const reconfirmAfterSignal = async (\n allowPendingRetry: boolean,\n ): Promise<void> => {\n const active = activeSessionRef.current;\n if (!active) return;\n try {\n const confirmation = await payApi.confirmPaymentMethod3DS({\n session_token: active.sessionToken,\n });\n if (activeSessionRef.current !== active) return;\n if (confirmation.status === \"approved\") {\n await finishApproved(confirmation);\n return;\n }\n // A second challenge after a completion signal is out of contract.\n finishFailed(t(\"payment_verification_didnt_complete\"));\n } catch (error) {\n if (activeSessionRef.current !== active) return;\n if (\n allowPendingRetry &&\n cardAdd3DSErrorCode(error) === \"three_ds_pending\"\n ) {\n await waitFor(PENDING_RETRY_DELAY_MS);\n if (activeSessionRef.current !== active) return;\n await reconfirmAfterSignal(false);\n return;\n }\n finishFailed(failureMessageFrom(error));\n }\n };\n\n const sessionStatusSnapshot = async (): Promise<ThreeDSStatus | null> => {\n const active = activeSessionRef.current;\n if (!active) return null;\n try {\n const snapshot = await payApi.fetchPaymentMethod3DSStatus({\n session_token: active.sessionToken,\n });\n return mapSessionStateToThreeDSStatus(snapshot.state, snapshot.expired);\n } catch {\n return null; // transient — keep polling\n }\n };\n\n useVgs3DSCompletion({\n channelName: session ? `private-3ds-session-${session.sessionToken}` : \"\",\n transport: POLLING_ONLY_TRANSPORT,\n checkStatus: sessionStatusSnapshot,\n merchantTxId: session?.merchantTxId,\n enabled: challenge !== null,\n onComplete: () => {\n void reconfirmAfterSignal(true);\n },\n onError: (message) => finishFailed(message),\n onCanceled: finishCanceled,\n });\n\n const closeChallengeModal = (timeoutError?: string) => {\n const active = activeSessionRef.current;\n // Fire-and-forget server-side cancel; the close never waits on it.\n if (active) {\n void payApi\n .cancelPaymentMethod3DS({ session_token: active.sessionToken })\n .catch(() => {});\n }\n if (timeoutError) finishFailed(timeoutError);\n else finishCanceled();\n };\n\n const modal = (\n <VgsThreeDSModal\n isOpen={challenge !== null}\n challengeHtml={challenge?.html ?? \"\"}\n challengeUrl={challenge?.url}\n onClose={closeChallengeModal}\n labels={{\n title: t(\"verify_your_payment\"),\n cancel: t(\"cancel\"),\n timedOut: t(\"payment_verification_timed_out\"),\n stillWorking: t(\"still_working_with_your_bank\"),\n dontRefresh: t(\"dont_refresh_this_tab\"),\n }}\n />\n );\n\n return { begin, modal, isActive: session !== null };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-portal-card-add-3ds-flow-DueNr2yx.mjs","names":["FormTextField","FormTextField","FormTextField"],"sources":["../../../profile/core/src/translation-api-context.ts","../src/providers/ProfileTranslationBridge.tsx","../../../profile/ui/src/components/ellipses-dropdown.tsx","../../../profile/ui/src/components/confirm-action-dialog.tsx","../../../profile/ui/src/components/form-fields.tsx","../../../profile/ui/src/components/user-info-dialog.tsx","../../../profile/ui/src/components/payment-method-card-internals.tsx","../../../profile/core/src/context.ts","../../../fluid-pay/core/src/context.ts","../../../fluid-pay/core/src/provider.tsx","../../../fluid-pay/core/src/utils/country-config.ts","../../../fluid-pay/core/src/hooks/use-country-states.ts","../../../profile/ui/src/components/edit-payment-method-dialog.tsx","../../../profile/ui/src/components/my-site-profile-section.tsx","../../../fluid-pay/ui/src/components/form-fields/FormTextField.tsx","../../../fluid-pay/ui/src/components/form-fields/FormComboboxField.tsx","../../../fluid-pay/ui/src/components/AddressFormDialog.tsx","../../../fluid-pay/ui/src/hooks/use-vgs-collect.ts","../../../fluid-pay/ui/src/components/CreditCardFormDialog.tsx","../../../fluid-pay/ui/src/components/VgsThreeDSModal.tsx","../../../fluid-pay/ui/src/hooks/three-ds-attempt.ts","../../../fluid-pay/ui/src/hooks/use-vgs-3ds-completion.ts","../../../fluid-pay/core/src/utils/browser-info.ts","../../core/src/pay-types.ts","../src/adapters/fluid-pay-api-adapter.ts","../../../platform/address-autocomplete/src/administrative-area.ts","../../../platform/address-autocomplete/src/components/AddressAutocompleteInput.tsx","../../../fluid-pay/core/src/utils/fingerprinting.ts","../src/hooks/three-ds-card-add-helpers.ts","../src/hooks/use-portal-card-add-3ds-flow.tsx"],"sourcesContent":["import type { Provider } from \"react\";\nimport { createTranslationContext } from \"@fluid-app/i18n/translation-api-context-factory\";\nimport type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport type { ProfileDict } from \"./translation-dictionary\";\n\nconst { Provider: ProfileProvider, useTranslation } =\n createTranslationContext<ProfileDict>(\"Profile\");\n\nexport const ProfileTranslationProvider: Provider<TranslationApi<ProfileDict> | null> =\n ProfileProvider;\nexport const useProfileTranslation = useTranslation;\n","import { useMemo, type ReactNode } from \"react\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useDomainDict } from \"@fluid-app/i18n/use-domain-dict\";\nimport {\n profileDomain,\n createProfileTranslationAdapter,\n} from \"@fluid-app/profile-core/translation-adapter\";\nimport { ProfileTranslationProvider } from \"@fluid-app/profile-core/translation-api-context\";\n\nexport function ProfileTranslationBridge({\n children,\n}: {\n children: ReactNode;\n}): React.JSX.Element {\n const { locale } = useActiveLocale();\n const dict = useDomainDict(profileDomain, locale);\n const api = useMemo(\n () => createProfileTranslationAdapter(locale, dict),\n [locale, dict],\n );\n return (\n <ProfileTranslationProvider value={api}>\n {children}\n </ProfileTranslationProvider>\n );\n}\n","import type { JSX } from \"react\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\nexport default function EllipsesDropdown({\n onDelete,\n onEdit,\n onMakeDefault,\n editLabel,\n deleteLabel,\n makeDefaultLabel,\n disabled = false,\n}: {\n onDelete?: () => void;\n onEdit?: () => void;\n onMakeDefault?: () => void;\n editLabel?: string;\n deleteLabel?: string;\n makeDefaultLabel?: string;\n disabled?: boolean;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const resolvedEditLabel = editLabel ?? t(\"edit\");\n const resolvedDeleteLabel = deleteLabel ?? t(\"delete\");\n const resolvedMakeDefaultLabel = makeDefaultLabel ?? t(\"make_default\");\n if (disabled) {\n return (\n <Button variant=\"ghost\" disabled>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 128 512\"\n className=\"fill-muted-foreground h-4 w-1\"\n >\n <path d=\"M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z\" />\n </svg>\n </Button>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 128 512\"\n className=\"fill-muted-foreground h-4 w-1\"\n >\n <path d=\"M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z\" />\n </svg>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"text-foreground min-w-28 space-y-1 text-sm\">\n {onEdit && (\n <>\n <DropdownMenuItem\n className=\"hover:bg-muted flex cursor-pointer flex-row items-center gap-x-2 rounded px-2\"\n onClick={(e) => {\n e.stopPropagation();\n onEdit();\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-3.5 flex-shrink-0\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10\"\n />\n </svg>\n <span>{resolvedEditLabel}</span>\n </DropdownMenuItem>\n {(onMakeDefault || onDelete) && <DropdownMenuSeparator />}\n </>\n )}\n {onMakeDefault && (\n <>\n <DropdownMenuItem\n className=\"hover:bg-muted flex cursor-pointer flex-row items-center gap-x-2 rounded px-2\"\n onClick={(e) => {\n e.stopPropagation();\n onMakeDefault();\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-3.5 flex-shrink-0\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m4.5 12.75 6 6 9-13.5\"\n />\n </svg>\n <span>{resolvedMakeDefaultLabel}</span>\n </DropdownMenuItem>\n {onDelete && <DropdownMenuSeparator />}\n </>\n )}\n {onDelete && (\n <DropdownMenuItem\n className=\"text-destructive hover:bg-destructive/10 flex cursor-pointer flex-row items-center gap-x-2 rounded px-2\"\n onClick={(e) => {\n e.stopPropagation();\n onDelete();\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-3.5 flex-shrink-0\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0\"\n />\n </svg>\n <span>{resolvedDeleteLabel}</span>\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","import type { JSX } from \"react\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n DialogClose,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\ntype ConfirmActionDialogProps = {\n title: string;\n description: string;\n onAction: () => void;\n openDialog: boolean;\n setOpenDialog: (open: boolean) => void;\n errorMsg?: string;\n isLoading?: boolean;\n actionText?: string;\n};\n\nexport default function ConfirmActionDialog({\n title,\n description,\n onAction,\n openDialog,\n setOpenDialog,\n errorMsg,\n isLoading,\n actionText,\n}: ConfirmActionDialogProps): JSX.Element {\n const { t } = useProfileTranslation();\n const resolvedActionText = actionText ?? t(\"delete\");\n\n return (\n <Dialog open={openDialog} onOpenChange={setOpenDialog}>\n <DialogContent className=\"max-w-sm md:w-90\">\n <DialogHeader className=\"flex flex-row justify-between\">\n <DialogTitle className=\"w-full text-left\">{title}</DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <p className=\"text-muted-foreground text-left text-sm\">\n {description}\n </p>\n </div>\n\n {errorMsg && <p className=\"text-destructive text-sm\">{errorMsg}</p>}\n\n <DialogFooter>\n <div className=\"flex w-full flex-row justify-between gap-x-2\">\n <DialogClose asChild>\n <Button\n variant=\"secondary\"\n className=\"min-w-[70px]\"\n onClick={() => setOpenDialog(false)}\n >\n {t(\"cancel\")}\n </Button>\n </DialogClose>\n <Button\n type=\"button\"\n variant=\"destructive\"\n className=\"min-w-[70px]\"\n onClick={onAction}\n disabled={isLoading}\n >\n {isLoading ? (\n <div className=\"border-primary-foreground size-5 animate-spin rounded-full border-4 border-t-4 border-t-transparent\" />\n ) : (\n resolvedActionText\n )}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import type React from \"react\";\nimport {\n useController,\n type Control,\n type FieldPath,\n type FieldValues,\n} from \"react-hook-form\";\nimport {\n Input,\n Label,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n Textarea,\n cn,\n} from \"@fluid-app/ui-primitives\";\n\nexport type NameValue = { name: string; value: string | number };\n\nexport function FormTextField<T extends FieldValues>({\n control,\n name,\n label,\n containerClassName,\n ...props\n}: {\n control: Control<T>;\n name: FieldPath<T>;\n label?: string;\n containerClassName?: string;\n} & Omit<React.ComponentProps<\"input\">, \"name\">): React.JSX.Element {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Input\n {...field}\n {...props}\n id={name}\n value={field.value ?? \"\"}\n className={cn(error && \"ring-destructive ring-1\", props.className)}\n />\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n\nexport function FormTextareaField<T extends FieldValues>({\n control,\n name,\n label,\n containerClassName,\n ...props\n}: {\n control: Control<T>;\n name: FieldPath<T>;\n label?: string;\n containerClassName?: string;\n} & Omit<React.ComponentProps<\"textarea\">, \"name\">): React.JSX.Element {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Textarea\n {...field}\n {...props}\n id={name}\n value={field.value ?? \"\"}\n className={cn(error && \"ring-destructive ring-1\", props.className)}\n />\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n\nexport function FormSelectField<T extends FieldValues>({\n control,\n name,\n label,\n options,\n placeholder,\n containerClassName,\n onChange,\n}: {\n control: Control<T>;\n name: FieldPath<T>;\n label?: string;\n options?: NameValue[];\n placeholder?: string;\n containerClassName?: string;\n onChange?: (value: string) => void;\n}): React.JSX.Element {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Select\n value={field.value?.toString() ?? \"\"}\n onValueChange={(val) => {\n field.onChange(val);\n onChange?.(val);\n }}\n >\n <SelectTrigger\n id={name}\n className={cn(\"w-full\", error && \"ring-destructive ring-1\")}\n >\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options?.map((opt) => (\n <SelectItem\n key={`${opt.name}-${opt.value}`}\n value={opt.value.toString()}\n >\n {opt.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n","import { useState, type JSX } from \"react\";\nimport type { Control } from \"react-hook-form\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n Button,\n Input,\n} from \"@fluid-app/ui-primitives\";\nimport { Info } from \"lucide-react\";\nimport {\n FormTextField,\n FormTextareaField,\n FormSelectField,\n type NameValue,\n} from \"./form-fields\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\nexport type UserFormData = {\n first_name: string;\n last_name: string;\n phone_number?: string;\n language: string;\n bio?: string;\n};\n\nexport default function UserInfoDialog({\n control,\n isOpen,\n onSubmit,\n handleClose,\n languageOptions,\n errorMsg,\n isSubmitting,\n email,\n onChangeEmail,\n isChangingEmail,\n pendingEmail,\n}: {\n control: Control<UserFormData>;\n isOpen: boolean;\n onSubmit: () => void;\n handleClose: () => void;\n languageOptions?: NameValue[];\n errorMsg: string | undefined;\n isSubmitting: boolean;\n email?: string;\n onChangeEmail?: (newEmail: string) => Promise<void> | void;\n isChangingEmail?: boolean;\n pendingEmail?: string;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const [isEditingEmail, setIsEditingEmail] = useState(false);\n const [newEmail, setNewEmail] = useState(\"\");\n const [emailError, setEmailError] = useState<string | undefined>(undefined);\n\n const handleEmailSubmit = async () => {\n setEmailError(undefined);\n const trimmed = newEmail.trim();\n if (!trimmed) {\n setEmailError(t(\"please_enter_new_email\"));\n return;\n }\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(trimmed)) {\n setEmailError(t(\"please_enter_valid_email\"));\n return;\n }\n if (email && trimmed.toLowerCase() === email.toLowerCase()) {\n setEmailError(t(\"new_email_must_be_different\"));\n return;\n }\n try {\n await onChangeEmail?.(trimmed);\n setIsEditingEmail(false);\n setNewEmail(\"\");\n } catch (err) {\n setEmailError(\n err instanceof Error\n ? err.message\n : t(\"failed_to_initiate_email_change\"),\n );\n }\n };\n\n const handleCancelEmailEdit = () => {\n setIsEditingEmail(false);\n setNewEmail(\"\");\n setEmailError(undefined);\n };\n\n const handleDialogClose = () => {\n handleCancelEmailEdit();\n handleClose();\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && handleDialogClose()}>\n <DialogContent className=\"max-w-sm md:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t(\"edit_profile\")}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <FormTextField\n control={control}\n name=\"first_name\"\n label={t(\"first_name\")}\n />\n <FormTextField\n control={control}\n name=\"last_name\"\n label={t(\"last_name\")}\n />\n <FormTextField\n control={control}\n name=\"phone_number\"\n label={t(\"phone_number\")}\n type=\"tel\"\n />\n <FormTextareaField\n control={control}\n name=\"bio\"\n label={t(\"bio\")}\n rows={3}\n placeholder={t(\"add_a_short_bio\")}\n />\n <FormSelectField\n control={control}\n name=\"language\"\n label={t(\"language\")}\n options={languageOptions}\n placeholder={t(\"select_an_option\")}\n />\n </div>\n {email && (\n <div className=\"space-y-2\">\n <label className=\"text-foreground text-sm font-medium\">\n {t(\"email\")}\n </label>\n {isEditingEmail ? (\n <div className=\"space-y-2\">\n <Input\n type=\"email\"\n placeholder={t(\"enter_new_email_placeholder\")}\n value={newEmail}\n onChange={(e) => {\n setNewEmail(e.target.value);\n setEmailError(undefined);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleEmailSubmit();\n }\n if (e.key === \"Escape\") {\n handleCancelEmailEdit();\n }\n }}\n aria-invalid={!!emailError || undefined}\n autoFocus\n />\n {emailError && (\n <p className=\"text-destructive text-xs\">{emailError}</p>\n )}\n <div className=\"bg-muted flex items-start gap-2.5 rounded-md px-3 py-2.5\">\n <Info className=\"text-primary mt-0.5 size-4 shrink-0\" />\n <p className=\"text-muted-foreground text-xs\">\n {t(\"email_verification_hint\")}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCancelEmailEdit}\n >\n {t(\"cancel\")}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={handleEmailSubmit}\n disabled={!newEmail.trim() || isChangingEmail}\n >\n {isChangingEmail && (\n <div className=\"mr-2 size-3.5 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isChangingEmail ? t(\"sending\") : t(\"send_verification\")}\n </Button>\n </div>\n </div>\n ) : pendingEmail ? (\n <div className=\"space-y-2\">\n <div className=\"bg-muted text-muted-foreground min-w-0 rounded-md px-3 py-2 text-sm\">\n {email}\n </div>\n <div className=\"bg-muted flex items-start gap-2.5 rounded-md px-3 py-2.5\">\n <Info className=\"text-primary mt-0.5 size-4 shrink-0\" />\n <p className=\"text-muted-foreground text-xs\">\n {t(\"changing_email_to\")}{\" \"}\n <span className=\"text-foreground font-medium\">\n {pendingEmail}\n </span>\n . {t(\"check_new_email_verification\")}\n </p>\n </div>\n </div>\n ) : (\n <div className=\"flex items-center gap-2\">\n <div className=\"bg-muted text-muted-foreground min-w-0 flex-1 rounded-md px-3 py-2 text-sm\">\n {email}\n </div>\n {onChangeEmail && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setIsEditingEmail(true)}\n className=\"shrink-0\"\n >\n {t(\"change\")}\n </Button>\n )}\n </div>\n )}\n </div>\n )}\n {errorMsg && <p className=\"text-destructive text-sm\">{errorMsg}</p>}\n <DialogFooter className=\"flex flex-row items-center justify-end\">\n <div className=\"flex-1 text-right\">\n <Button type=\"button\" onClick={onSubmit}>\n {isSubmitting && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isSubmitting ? t(\"saving\") : t(\"save_changes\")}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import { useState, type JSX } from \"react\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport {\n brandIconUrl as resolveBrandIconUrl,\n normalizeCardBrand,\n} from \"@fluid-app/payments-display/brands\";\nimport {\n isCardPaymentDescriptor,\n normalizePaymentDescriptor,\n paymentDescriptorLabel,\n resolvePaymentLogo,\n resolvePaymentMethodDisplay,\n} from \"@fluid-app/payments-display/display\";\n\nexport function brandIconUrl(brand: string | null | undefined): string {\n return resolveBrandIconUrl(brand);\n}\n\nexport function PaymentIcon({\n logoUrl,\n brand,\n alt,\n}: {\n logoUrl?: string;\n brand: string | null | undefined;\n alt: string;\n}): JSX.Element {\n const logo = resolvePaymentLogo({ brand, logoUrl, alt });\n const [hasError, setHasError] = useState(false);\n const src = hasError ? logo.fallbackUrl : logo.primaryUrl;\n return (\n <img\n src={src}\n alt={logo.alt}\n width={40}\n height={24}\n className=\"object-contain\"\n onError={() => setHasError(true)}\n />\n );\n}\n\nexport function getPaymentMethodLogoBrand(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n): string | null {\n if (isCardPaymentMethod(paymentMethod)) {\n return (\n normalizeCardBrand(paymentMethod.details.card_brand) ??\n normalizeCardBrand(paymentMethod.details.card_network)\n );\n }\n\n return paymentDescriptorLabel(\n paymentMethod.payment_type,\n paymentMethod.source,\n );\n}\n\nexport function getCardDisplayName(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n cardFallback = \"Card\",\n): string {\n if (isCardPaymentMethod(paymentMethod)) {\n return resolvePaymentMethodDisplay({\n kind: \"card\",\n brand: getPaymentMethodLogoBrand(paymentMethod) ?? cardFallback,\n last4: paymentMethod.details.last_four,\n logoUrl: paymentMethod.details.logo_url,\n }).label;\n }\n\n const fallbackLabel = paymentDescriptorLabel(\n paymentMethod.payment_type,\n paymentMethod.source,\n );\n return resolvePaymentMethodDisplay({\n kind: \"non_card\",\n brand: fallbackLabel,\n fallbackLabel,\n logoUrl: paymentMethod.details.logo_url,\n }).label;\n}\n\nexport function getCardExpiry(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n expiresLabel = \"Expires\",\n): string {\n const { exp_month, exp_year } = paymentMethod.details;\n if (exp_month != null && exp_year != null) {\n return `${expiresLabel} ${exp_month}/${exp_year}`;\n }\n return \"\";\n}\n\nfunction isCardPaymentMethod(\n paymentMethod: fluidPay.CustomerPaymentMethod,\n): boolean {\n return (\n paymentMethod.source === \"card\" ||\n isCardPaymentDescriptor(paymentMethod.payment_type) ||\n isCardPaymentDescriptor(paymentMethod.source) ||\n normalizePaymentDescriptor(paymentMethod.details.card_brand) != null ||\n normalizePaymentDescriptor(paymentMethod.details.card_network) != null ||\n paymentMethod.details.last_four != null\n );\n}\n","import { createContext, useContext } from \"react\";\n\nexport type TranslationFn = (key: string) => string;\n\nexport interface ProfileUIContextValue {\n t: TranslationFn;\n}\n\nexport const ProfileUIContext = createContext<ProfileUIContextValue | null>(\n null,\n);\n\nexport function useProfileUI(): ProfileUIContextValue {\n const context = useContext(ProfileUIContext);\n if (!context) {\n throw new Error(\"useProfileUI must be used within a ProfileUIProvider\");\n }\n return context;\n}\n","import { createContext, useContext } from \"react\";\nimport type { FluidPayApi } from \"./api-interface\";\n\nconst FluidPayApiContext = createContext<FluidPayApi | null>(null);\n\nexport const FluidPayApiProvider = FluidPayApiContext.Provider;\n\nexport function useFluidPayApi(): FluidPayApi {\n const api = useContext(FluidPayApiContext);\n if (!api) {\n throw new Error(\n \"useFluidPayApi must be used within a FluidPayCoreProvider\",\n );\n }\n return api;\n}\n","import type { JSX } from \"react\";\nimport type { FluidPayApi } from \"./api-interface\";\nimport { FluidPayApiProvider } from \"./context\";\n\nexport interface FluidPayCoreProviderProps {\n api: FluidPayApi;\n children: React.ReactNode;\n}\n\nexport function FluidPayCoreProvider({\n api,\n children,\n}: FluidPayCoreProviderProps): JSX.Element {\n return <FluidPayApiProvider value={api}>{children}</FluidPayApiProvider>;\n}\n","export interface State {\n name: string;\n isoCode: string;\n}\n\nexport interface RegionConfig {\n regionLabel: string;\n postalLabel: string;\n}\n\nexport const US_STATES: State[] = [\n { name: \"Alabama\", isoCode: \"AL\" },\n { name: \"Alaska\", isoCode: \"AK\" },\n { name: \"Arizona\", isoCode: \"AZ\" },\n { name: \"Arkansas\", isoCode: \"AR\" },\n { name: \"California\", isoCode: \"CA\" },\n { name: \"Colorado\", isoCode: \"CO\" },\n { name: \"Connecticut\", isoCode: \"CT\" },\n { name: \"Delaware\", isoCode: \"DE\" },\n { name: \"Florida\", isoCode: \"FL\" },\n { name: \"Georgia\", isoCode: \"GA\" },\n { name: \"Hawaii\", isoCode: \"HI\" },\n { name: \"Idaho\", isoCode: \"ID\" },\n { name: \"Illinois\", isoCode: \"IL\" },\n { name: \"Indiana\", isoCode: \"IN\" },\n { name: \"Iowa\", isoCode: \"IA\" },\n { name: \"Kansas\", isoCode: \"KS\" },\n { name: \"Kentucky\", isoCode: \"KY\" },\n { name: \"Louisiana\", isoCode: \"LA\" },\n { name: \"Maine\", isoCode: \"ME\" },\n { name: \"Maryland\", isoCode: \"MD\" },\n { name: \"Massachusetts\", isoCode: \"MA\" },\n { name: \"Michigan\", isoCode: \"MI\" },\n { name: \"Minnesota\", isoCode: \"MN\" },\n { name: \"Mississippi\", isoCode: \"MS\" },\n { name: \"Missouri\", isoCode: \"MO\" },\n { name: \"Montana\", isoCode: \"MT\" },\n { name: \"Nebraska\", isoCode: \"NE\" },\n { name: \"Nevada\", isoCode: \"NV\" },\n { name: \"New Hampshire\", isoCode: \"NH\" },\n { name: \"New Jersey\", isoCode: \"NJ\" },\n { name: \"New Mexico\", isoCode: \"NM\" },\n { name: \"New York\", isoCode: \"NY\" },\n { name: \"North Carolina\", isoCode: \"NC\" },\n { name: \"North Dakota\", isoCode: \"ND\" },\n { name: \"Ohio\", isoCode: \"OH\" },\n { name: \"Oklahoma\", isoCode: \"OK\" },\n { name: \"Oregon\", isoCode: \"OR\" },\n { name: \"Pennsylvania\", isoCode: \"PA\" },\n { name: \"Rhode Island\", isoCode: \"RI\" },\n { name: \"South Carolina\", isoCode: \"SC\" },\n { name: \"South Dakota\", isoCode: \"SD\" },\n { name: \"Tennessee\", isoCode: \"TN\" },\n { name: \"Texas\", isoCode: \"TX\" },\n { name: \"Utah\", isoCode: \"UT\" },\n { name: \"Vermont\", isoCode: \"VT\" },\n { name: \"Virginia\", isoCode: \"VA\" },\n { name: \"Washington\", isoCode: \"WA\" },\n { name: \"West Virginia\", isoCode: \"WV\" },\n { name: \"Wisconsin\", isoCode: \"WI\" },\n { name: \"Wyoming\", isoCode: \"WY\" },\n { name: \"District of Columbia\", isoCode: \"DC\" },\n];\n\nexport const COUNTRY_CONFIGS: Record<string, RegionConfig> = {\n US: { regionLabel: \"State\", postalLabel: \"ZIP Code\" },\n JP: { regionLabel: \"Prefecture\", postalLabel: \"Postal Code\" },\n};\n\nconst DEFAULT_REGION_CONFIG: RegionConfig = {\n regionLabel: \"Province\",\n postalLabel: \"Postal Code\",\n};\n\nexport function getRegionConfig(countryCode: string): RegionConfig {\n return COUNTRY_CONFIGS[countryCode] ?? DEFAULT_REGION_CONFIG;\n}\n\nexport const DEFAULT_ADDRESS_FIELDS = [\n { field: \"name\", label: \"Name\", required: true },\n { field: \"address1\", label: \"Address\", required: true },\n { field: \"address2\", label: \"Apartment, suite, etc.\", required: false },\n { field: \"city\", label: \"City\", required: true },\n { field: \"state\", label: \"State\", required: true },\n { field: \"postal_code\", label: \"Postal Code\", required: true },\n { field: \"country_code\", label: \"Country\", required: true },\n];\n","import { useCallback, useState } from \"react\";\nimport {\n US_STATES,\n getRegionConfig,\n type State,\n} from \"../utils/country-config\";\n\nexport default function useCountryStates() {\n const [selectedCountry, setSelectedCountry] = useState<string>(\"US\");\n const [stateOptions, setStateOptions] = useState<State[]>(US_STATES);\n\n const handleCountryChange = useCallback(\n (\n selectedCountryCode: string,\n fetchStates?: (countryCode: string) => Promise<State[]>,\n ) => {\n setSelectedCountry(selectedCountryCode);\n const fallbackStates = selectedCountryCode === \"US\" ? US_STATES : [];\n if (fetchStates) {\n fetchStates(selectedCountryCode)\n .then((states) =>\n setStateOptions(states.length > 0 ? states : fallbackStates),\n )\n .catch((error) => {\n console.error(\"Failed to fetch states:\", error);\n setStateOptions(fallbackStates);\n });\n } else if (selectedCountryCode === \"US\") {\n setStateOptions(US_STATES);\n } else {\n setStateOptions([]);\n }\n },\n [],\n );\n\n const config = getRegionConfig(selectedCountry);\n\n return { handleCountryChange, stateOptions, config, selectedCountry };\n}\n","import { useEffect, useMemo, useState, type JSX } from \"react\";\nimport { useWatch } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { Trash2 } from \"lucide-react\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n Button,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport { FormTextField, FormSelectField } from \"./form-fields\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport { useCountryStates } from \"@fluid-app/profile-core\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\nimport type { BillingAddress } from \"./view-payment-method-dialog\";\nimport {\n PaymentIcon,\n getCardDisplayName,\n getPaymentMethodLogoBrand,\n} from \"./payment-method-card-internals\";\n\nfunction createEditPaymentMethodFormSchema(t: (key: string) => string) {\n return z.object({\n billing_address: z.object({\n name: z.string().min(1, t(\"name_is_required\")),\n address1: z.string().min(1, t(\"address_line_1_is_required\")),\n address2: z.string().optional().nullable(),\n city: z.string().min(1, t(\"city_is_required\")),\n state: z.string().min(1, t(\"state_is_required\")),\n zip: z.string().min(1, t(\"zip_code_is_required\")),\n country_code: z.string().min(1, t(\"country_is_required\")),\n }),\n set_as_default: z.boolean(),\n });\n}\n\nexport type EditPaymentMethodFormData = z.infer<\n ReturnType<typeof createEditPaymentMethodFormSchema>\n>;\n\ninterface CountryOption {\n iso: string;\n name: string;\n}\n\ninterface EditPaymentMethodDialogProps {\n isOpen: boolean;\n paymentMethod: fluidPay.CustomerPaymentMethod | null;\n billingAddress?: BillingAddress | null;\n onClose: () => void;\n onSubmit: (data: EditPaymentMethodFormData) => void;\n isSubmitting: boolean;\n error?: string;\n countries?: CountryOption[];\n onDelete?: () => void;\n isDeleting?: boolean;\n}\n\nexport default function EditPaymentMethodDialog({\n isOpen,\n paymentMethod,\n billingAddress,\n onClose,\n onSubmit,\n isSubmitting,\n error,\n countries = [],\n onDelete,\n isDeleting = false,\n}: EditPaymentMethodDialogProps): JSX.Element | null {\n const { t } = useProfileTranslation();\n const [confirmDelete, setConfirmDelete] = useState(false);\n\n useEffect(() => {\n if (!isOpen) setConfirmDelete(false);\n }, [isOpen]);\n\n useEffect(() => {\n if (!confirmDelete) return;\n const timeoutId = setTimeout(() => setConfirmDelete(false), 3000);\n return () => clearTimeout(timeoutId);\n }, [confirmDelete]);\n\n const editPaymentMethodFormSchema = useMemo(\n () => createEditPaymentMethodFormSchema((key) => t(key as never)),\n [t],\n );\n\n const { control, handleSubmit, reset, setValue } =\n useZodForm<EditPaymentMethodFormData>(editPaymentMethodFormSchema, {\n defaultValues: {\n billing_address: {\n name: \"\",\n address1: \"\",\n address2: \"\",\n city: \"\",\n state: \"\",\n zip: \"\",\n country_code: \"US\",\n },\n set_as_default: false,\n },\n });\n\n const billingCountry = useWatch({\n control,\n name: \"billing_address.country_code\",\n });\n const setAsDefault = useWatch({ control, name: \"set_as_default\" });\n\n const { stateOptions, config, handleCountryChange } = useCountryStates();\n\n const countryOptions = [...countries]\n .map((country) => ({\n name: country.name,\n value: country.iso,\n }))\n .sort((a, b) => {\n if (a.value === billingCountry) return -1;\n if (b.value === billingCountry) return 1;\n return a.name.localeCompare(b.name);\n });\n\n const stateSelectOptions = stateOptions.map((state) => ({\n name: state.name,\n value: state.isoCode,\n }));\n\n useEffect(() => {\n if (paymentMethod && isOpen) {\n reset({\n billing_address: {\n name: billingAddress?.name ?? \"\",\n address1: billingAddress?.address1 ?? \"\",\n address2: billingAddress?.address2 ?? \"\",\n city: billingAddress?.city ?? \"\",\n state: billingAddress?.state ?? \"\",\n zip: billingAddress?.zip ?? \"\",\n country_code: billingAddress?.country_code ?? \"US\",\n },\n set_as_default: paymentMethod.default,\n });\n }\n }, [paymentMethod, billingAddress, isOpen, reset]);\n\n const handleCountrySelect = (countryCode: string) => {\n handleCountryChange(countryCode);\n setValue(\"billing_address.country_code\", countryCode, {\n shouldValidate: true,\n });\n };\n\n const handleFormSubmit = handleSubmit((data) => {\n onSubmit(data);\n });\n\n const deleteButtonLabel = useMemo(() => {\n if (isDeleting) return t(\"deleting\");\n if (confirmDelete) return t(\"tap_to_confirm\");\n return t(\"delete_card\");\n }, [isDeleting, confirmDelete, t]);\n\n if (!paymentMethod) return null;\n\n const { details } = paymentMethod;\n const displayName = getCardDisplayName(paymentMethod, t(\"card_fallback\"));\n const expMonth = details.exp_month;\n const expYear = details.exp_year;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"max-h-[90vh] max-w-sm overflow-y-auto md:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{t(\"edit_card\")}</DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"bg-muted rounded-lg p-4\">\n <div className=\"flex items-center gap-x-3\">\n <div className=\"h-6 w-10 flex-shrink-0\">\n <PaymentIcon\n logoUrl={details.logo_url}\n brand={getPaymentMethodLogoBrand(paymentMethod)}\n alt={displayName}\n />\n </div>\n <div className=\"flex flex-col\">\n <span className=\"text-foreground text-sm font-medium\">\n {displayName}\n </span>\n {expMonth != null && expYear != null && (\n <span className=\"text-muted-foreground text-sm\">\n {t(\"card_expires\")} {expMonth}/{expYear}\n </span>\n )}\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"text-foreground mb-3 text-sm font-medium\">\n {t(\"billing_address\")}\n </h3>\n <div className=\"space-y-3\">\n <FormSelectField\n control={control}\n name=\"billing_address.country_code\"\n label={t(\"country\")}\n options={countryOptions}\n placeholder={t(\"select_an_option\")}\n onChange={(val) => {\n handleCountrySelect(val);\n }}\n />\n <FormTextField\n control={control}\n name=\"billing_address.name\"\n label={t(\"name\")}\n />\n <FormTextField\n control={control}\n name=\"billing_address.address1\"\n label={t(\"address_line_1\")}\n />\n <FormTextField\n control={control}\n name=\"billing_address.address2\"\n label={t(\"address_line_2\")}\n />\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <FormTextField\n control={control}\n name=\"billing_address.city\"\n label={t(\"city\")}\n />\n <FormSelectField\n control={control}\n name=\"billing_address.state\"\n label={\n config?.regionLabel\n ? t(config.regionLabel as never)\n : t(\"state\")\n }\n options={stateSelectOptions}\n placeholder={t(\"select_state\")}\n />\n <FormTextField\n control={control}\n name=\"billing_address.zip\"\n label={\n config?.postalLabel\n ? t(config.postalLabel as never)\n : t(\"zip_code\")\n }\n />\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"mt-2 flex items-center gap-x-2\">\n <input\n type=\"checkbox\"\n id=\"set_as_default\"\n checked={setAsDefault}\n onChange={() => setValue(\"set_as_default\", !setAsDefault)}\n className=\"size-4\"\n />\n <label htmlFor=\"set_as_default\" className=\"text-sm\">\n {t(\"set_as_default_payment_method\")}\n </label>\n </div>\n\n {error && <div className=\"text-destructive text-sm\">{error}</div>}\n\n <DialogFooter className=\"flex flex-row items-center justify-between gap-2\">\n {onDelete ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => {\n if (confirmDelete) {\n onDelete();\n } else {\n setConfirmDelete(true);\n }\n }}\n disabled={isSubmitting || isDeleting}\n className=\"text-destructive hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2 className=\"size-4\" />\n {deleteButtonLabel}\n </Button>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={onClose}\n disabled={isSubmitting || isDeleting}\n >\n {t(\"cancel\")}\n </Button>\n <Button\n type=\"button\"\n onClick={handleFormSubmit}\n disabled={isSubmitting || isDeleting}\n >\n {isSubmitting && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isSubmitting ? t(\"saving\") : t(\"save_changes\")}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import type React from \"react\";\nimport { useController, type Control } from \"react-hook-form\";\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Label,\n Textarea,\n cn,\n} from \"@fluid-app/ui-primitives\";\nimport { z } from \"zod\";\nimport { FormTextField } from \"./form-fields\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\n\nfunction normalizeUrl(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (/^https?:\\/\\//i.test(trimmed)) return trimmed;\n return `https://${trimmed}`;\n}\n\nconst urlField = z\n .string()\n .transform(normalizeUrl)\n .pipe(z.string().url().or(z.literal(\"\")));\n\nexport const mySiteProfileSchema = z.object({\n bio: z.string().optional(),\n linkedin: urlField,\n facebook: urlField,\n twitter: urlField,\n instagram: urlField,\n youtube: urlField,\n pinterest: urlField,\n tiktok: urlField,\n whatsapp: z.string().optional(),\n wechat: z.string().optional(),\n});\n\nexport type MySiteProfileFormData = z.infer<typeof mySiteProfileSchema>;\n\nexport const socialFields = [\n {\n name: \"linkedin\",\n label: \"LinkedIn\",\n placeholder: \"linkedin.com/in/username\",\n },\n { name: \"facebook\", label: \"Facebook\", placeholder: \"facebook.com/username\" },\n { name: \"twitter\", label: \"X (Twitter)\", placeholder: \"x.com/username\" },\n {\n name: \"instagram\",\n label: \"Instagram\",\n placeholder: \"instagram.com/username\",\n },\n { name: \"youtube\", label: \"YouTube\", placeholder: \"youtube.com/@channel\" },\n {\n name: \"pinterest\",\n label: \"Pinterest\",\n placeholder: \"pinterest.com/username\",\n },\n { name: \"tiktok\", label: \"TikTok\", placeholder: \"tiktok.com/@username\" },\n { name: \"whatsapp\", label: \"WhatsApp\", placeholder: \"Phone number or link\" },\n { name: \"wechat\", label: \"WeChat\", placeholder: \"WeChat ID\" },\n] as const satisfies readonly {\n name: keyof Omit<MySiteProfileFormData, \"bio\">;\n label: string;\n placeholder: string;\n}[];\n\nexport interface MySiteProfileSectionProps {\n avatarUrl?: string | null;\n initial?: string;\n control: Control<MySiteProfileFormData>;\n}\n\nfunction BioField({ control }: { control: Control<MySiteProfileFormData> }) {\n const { t } = useProfileTranslation();\n const {\n field,\n fieldState: { error },\n } = useController({ name: \"bio\", control });\n return (\n <div className=\"space-y-1\">\n <Label htmlFor=\"mysite-bio\" className=\"block text-sm font-medium\">\n {t(\"bio\")}\n </Label>\n <Textarea\n id=\"mysite-bio\"\n rows={4}\n placeholder={t(\"bio_placeholder\")}\n className={cn(error && \"ring-destructive ring-1\")}\n {...field}\n value={field.value ?? \"\"}\n />\n {error && <p className=\"text-destructive text-sm\">{error.message}</p>}\n </div>\n );\n}\n\nexport function MySiteProfileSection({\n avatarUrl,\n initial,\n control,\n}: MySiteProfileSectionProps): React.JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <Card>\n <CardHeader>\n <CardTitle>{t(\"about_me\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-col gap-6 lg:flex-row lg:gap-10\">\n <div className=\"bg-muted relative h-[120px] w-[120px] shrink-0 self-center overflow-hidden rounded-full lg:self-auto\">\n {avatarUrl ? (\n <img\n src={avatarUrl}\n alt={t(\"profile_image_alt\")}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"bg-background text-foreground flex h-full w-full items-center justify-center text-xl font-semibold\">\n {initial ?? \"U\"}\n </div>\n )}\n </div>\n\n <div className=\"min-w-0 flex-1\">\n <BioField control={control} />\n </div>\n </CardContent>\n </Card>\n );\n}\n\nexport interface MySiteProfileSocialSectionProps {\n control: Control<MySiteProfileFormData>;\n}\n\nexport function MySiteProfileSocialSection({\n control,\n}: MySiteProfileSocialSectionProps): React.JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <Card>\n <CardHeader>\n <CardTitle>{t(\"social_media_links\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n {socialFields.map((field) => {\n let placeholder: string = field.placeholder;\n if (field.name === \"whatsapp\")\n placeholder = t(\"whatsapp_placeholder\");\n if (field.name === \"wechat\") placeholder = t(\"wechat_placeholder\");\n return (\n <FormTextField\n key={field.name}\n control={control}\n name={field.name}\n label={field.label}\n placeholder={placeholder}\n />\n );\n })}\n </div>\n </CardContent>\n </Card>\n );\n}\n\nexport interface MySiteProfileSaveButtonProps {\n isDirty: boolean;\n isPending: boolean;\n onSubmit: () => void;\n}\n\nexport function MySiteProfileSaveButton({\n isDirty,\n isPending,\n onSubmit,\n}: MySiteProfileSaveButtonProps): React.JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <div className=\"flex justify-end pb-6\">\n <Button\n type=\"button\"\n onClick={onSubmit}\n disabled={!isDirty || isPending}\n className=\"w-full sm:w-auto\"\n >\n {isPending && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isPending ? t(\"saving\") : t(\"save\")}\n </Button>\n </div>\n );\n}\n","\"use client\";\n\nimport type { Control, FieldPath, FieldValues } from \"react-hook-form\";\nimport { useController } from \"react-hook-form\";\nimport { Input, Label, cn } from \"@fluid-app/ui-primitives\";\n\ninterface FormTextFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n control: Control<TFieldValues>;\n name: TName;\n label?: string;\n placeholder?: string;\n containerClassName?: string;\n type?: React.HTMLInputTypeAttribute;\n disabled?: boolean;\n inputMode?: React.HTMLAttributes<HTMLInputElement>[\"inputMode\"];\n autoComplete?: string;\n}\n\nexport function FormTextField<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n control,\n name,\n label,\n placeholder,\n containerClassName,\n type,\n disabled,\n inputMode,\n autoComplete,\n}: FormTextFieldProps<TFieldValues, TName>) {\n const {\n field,\n fieldState: { error },\n } = useController({ name, control });\n\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Input\n {...field}\n value={field.value ?? \"\"}\n id={name}\n type={type}\n disabled={disabled}\n inputMode={inputMode}\n placeholder={placeholder}\n autoComplete={autoComplete}\n aria-invalid={!!error}\n />\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport type { Control, FieldPath, FieldValues } from \"react-hook-form\";\nimport { Controller } from \"react-hook-form\";\nimport { Label, cn } from \"@fluid-app/ui-primitives\";\nimport { Combobox } from \"@fluid-app/ui-components/components/Combobox\";\n\ntype NameValue = { name: string; value: string | number };\n\ninterface FormComboboxFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n control: Control<TFieldValues>;\n name: TName;\n label?: string;\n placeholder?: string;\n emptyText?: string;\n options?: NameValue[];\n containerClassName?: string;\n disabled?: boolean;\n}\n\nexport function FormComboboxField<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n control,\n name,\n label,\n placeholder = \"Select\",\n emptyText = \"No results found.\",\n options,\n containerClassName,\n disabled,\n}: FormComboboxFieldProps<TFieldValues, TName>) {\n const comboboxOptions = useMemo(\n () =>\n options?.map((opt) => ({\n label: opt.name,\n value: opt.value.toString(),\n })) ?? [],\n [options],\n );\n\n return (\n <div className={cn(\"space-y-1\", containerClassName)}>\n {label && (\n <Label htmlFor={name} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <Controller\n control={control}\n name={name}\n render={({ field: { onChange, value }, fieldState: { error } }) => (\n <>\n <Combobox\n options={comboboxOptions}\n value={value?.toString() ?? \"\"}\n onValueChange={onChange}\n placeholder={placeholder}\n emptyText={emptyText}\n disabled={disabled}\n />\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </>\n )}\n />\n </div>\n );\n}\n","import {\n type JSX,\n type ReactNode,\n useEffect,\n useId,\n useMemo,\n useState,\n} from \"react\";\nimport {\n Controller,\n useWatch,\n type Control,\n type UseFormSetValue,\n} from \"react-hook-form\";\nimport { Trash2 } from \"lucide-react\";\nimport { z } from \"zod\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Button,\n Label,\n cn,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport { FormTextField } from \"./form-fields/FormTextField\";\nimport { FormComboboxField } from \"./form-fields/FormComboboxField\";\nimport { PhoneInput } from \"@fluid-app/ui-components/components/PhoneInput\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport { useCountryStates, type State } from \"@fluid-app/fluid-pay-core\";\nimport { useAddressFieldsApiOptional } from \"@fluid-app/address-fields/address-fields-api-context\";\nimport type { AddressField } from \"@fluid-app/address-fields/types\";\n\nconst addressFormSchema = z.object({\n name: z.string(),\n address1: z.string(),\n address2: z.string().optional().nullable(),\n city: z.string(),\n state: z.string(),\n postal_code: z.string(),\n phone: z.string().optional().nullable(),\n country_code: z.string().min(1, \"Country is required\"),\n default: z.boolean(),\n});\n\nexport type AddressFormData = z.infer<typeof addressFormSchema>;\n\ninterface CountryOption {\n iso: string;\n name: string;\n}\n\nexport interface AddressAutocompleteRenderProps {\n control: Control<AddressFormData>;\n setValue: UseFormSetValue<AddressFormData>;\n countryCode: string;\n stateOptions: readonly State[];\n}\n\ninterface AddressFormDialogProps {\n isOpen: boolean;\n onClose: () => void;\n selectedAddress: fluidPay.CustomerAddress | null;\n onSubmit: (data: fluidPay.CreateAddressBody) => void;\n isSubmitting: boolean;\n countries?: CountryOption[];\n error?: string;\n renderAddressAutocomplete?: (\n props: AddressAutocompleteRenderProps,\n ) => ReactNode;\n fetchStates?: (countryCode: string) => Promise<State[]>;\n t: (key: string) => string;\n onDelete?: () => void;\n isDeleting?: boolean;\n defaultCountry?: string;\n languageIso?: string;\n}\n\ntype AddressFieldName = keyof Omit<AddressFormData, \"default\" | \"country_code\">;\n\nconst STATIC_FIELD_ORDER: AddressFieldName[] = [\n \"name\",\n \"address1\",\n \"address2\",\n \"city\",\n \"state\",\n \"postal_code\",\n \"phone\",\n];\n\nconst STATIC_LABELS: Record<AddressFieldName, string> = {\n name: \"name\",\n address1: \"address_line_1\",\n address2: \"address_line_2\",\n city: \"city\",\n state: \"state\",\n postal_code: \"zip_code\",\n phone: \"phone_number\",\n};\n\nconst DEFAULT_HIDDEN_FIELDS = new Set<AddressFieldName>([\"phone\"]);\nconst STATIC_REQUIRED_FIELDS = new Set<AddressFieldName>([\n \"name\",\n \"address1\",\n \"city\",\n \"state\",\n \"postal_code\",\n]);\nconst REQUIRED_FIELD_MESSAGES: Record<AddressFieldName, string> = {\n name: \"Name is required\",\n address1: \"Address line 1 is required\",\n address2: \"Address line 2 is required\",\n city: \"City is required\",\n state: \"State is required\",\n postal_code: \"Postal code is required\",\n phone: \"Phone is required\",\n};\nconst AUTOCOMPLETE_FIELD_PREFIX: readonly AddressFieldName[] = [\n \"name\",\n \"address1\",\n \"address2\",\n];\n\nfunction isAddressFieldName(value: string): value is AddressFieldName {\n return STATIC_FIELD_ORDER.includes(value as AddressFieldName);\n}\n\nfunction prioritizeAutocompleteFields(\n fields: readonly AddressFieldName[],\n): AddressFieldName[] {\n const prefix = AUTOCOMPLETE_FIELD_PREFIX.filter((field) =>\n fields.includes(field),\n );\n const rest = fields.filter(\n (field) => !AUTOCOMPLETE_FIELD_PREFIX.includes(field),\n );\n return [...prefix, ...rest];\n}\n\nfunction groupFields(fields: AddressFieldName[]): AddressFieldName[][] {\n const groups: AddressFieldName[][] = [];\n let i = 0;\n while (i < fields.length) {\n const current = fields[i]!;\n\n const locationFields = new Set<AddressFieldName>([\n \"city\",\n \"state\",\n \"postal_code\",\n ]);\n if (locationFields.has(current)) {\n const group: AddressFieldName[] = [current];\n while (\n i + group.length < fields.length &&\n locationFields.has(fields[i + group.length]!)\n ) {\n group.push(fields[i + group.length]!);\n }\n if (group.length > 1) {\n groups.push(group);\n i += group.length;\n continue;\n }\n }\n\n groups.push([current]);\n i++;\n }\n return groups;\n}\n\nfunction isFieldRequired(\n bffFields: readonly AddressField[] | null,\n fieldName: AddressFieldName,\n): boolean {\n if (!bffFields) return STATIC_REQUIRED_FIELDS.has(fieldName);\n return (\n bffFields.find((field) => field.field === fieldName)?.required ?? false\n );\n}\n\nfunction isBlank(value: string | null | undefined): boolean {\n return value == null || value.trim().length === 0;\n}\n\nexport default function AddressFormDialog({\n isOpen,\n onClose,\n selectedAddress,\n onSubmit,\n isSubmitting,\n countries = [],\n error,\n renderAddressAutocomplete,\n fetchStates,\n t,\n onDelete,\n isDeleting = false,\n defaultCountry = \"US\",\n languageIso,\n}: AddressFormDialogProps): JSX.Element {\n const [confirmDelete, setConfirmDelete] = useState(false);\n const phoneInputId = useId();\n const addressFieldsApi = useAddressFieldsApiOptional();\n const [bffFields, setBffFields] = useState<AddressField[] | null>(null);\n\n useEffect(() => {\n if (!isOpen) setConfirmDelete(false);\n }, [isOpen]);\n\n useEffect(() => {\n if (!confirmDelete) return;\n const timeoutId = setTimeout(() => setConfirmDelete(false), 3000);\n return () => clearTimeout(timeoutId);\n }, [confirmDelete]);\n\n const deleteButtonLabel = useMemo(() => {\n if (isDeleting) return t(\"deleting\");\n if (confirmDelete) return t(\"tap_to_confirm\");\n return t(\"delete_address\");\n }, [isDeleting, confirmDelete, t]);\n\n const { control, handleSubmit, reset, setValue, setError, clearErrors } =\n useZodForm<AddressFormData>(addressFormSchema, {\n defaultValues: {\n name: \"\",\n address1: \"\",\n address2: \"\",\n city: \"\",\n state: \"\",\n postal_code: \"\",\n phone: \"\",\n country_code: defaultCountry,\n default: false,\n },\n });\n\n const selectedCountryCode = useWatch({ control, name: \"country_code\" });\n const isDefault = useWatch({ control, name: \"default\" });\n\n const { stateOptions, config, handleCountryChange } = useCountryStates();\n const hasAddressAutocomplete = Boolean(renderAddressAutocomplete);\n\n useEffect(() => {\n if (!addressFieldsApi || !selectedCountryCode) return;\n let cancelled = false;\n setBffFields(null);\n addressFieldsApi\n .fetchAddressFields(selectedCountryCode, languageIso)\n .then((response) => {\n if (!cancelled) {\n setBffFields([...response.address_fields.fields]);\n }\n })\n .catch(() => {\n if (!cancelled) setBffFields(null);\n });\n return () => {\n cancelled = true;\n };\n }, [addressFieldsApi, selectedCountryCode, languageIso]);\n\n const fieldOrder = useMemo<AddressFieldName[]>(() => {\n if (!bffFields)\n return STATIC_FIELD_ORDER.filter((f) => !DEFAULT_HIDDEN_FIELDS.has(f));\n const fieldOrder = bffFields.map((f) => f.field).filter(isAddressFieldName);\n return hasAddressAutocomplete\n ? prioritizeAutocompleteFields(fieldOrder)\n : fieldOrder;\n }, [bffFields, hasAddressAutocomplete]);\n\n const fieldGroups = useMemo(() => groupFields(fieldOrder), [fieldOrder]);\n\n const getFieldLabel = (fieldName: AddressFieldName): string => {\n if (bffFields) {\n const bffField = bffFields.find((f) => f.field === fieldName);\n if (bffField) return bffField.label;\n }\n if (fieldName === \"state\" && config?.regionLabel) {\n return t(config.regionLabel);\n }\n if (fieldName === \"postal_code\" && config?.postalLabel) {\n return t(config.postalLabel);\n }\n const key = STATIC_LABELS[fieldName];\n return key ? t(key) : fieldName;\n };\n\n const countrySelectOptions = [...countries]\n .map((country) => ({\n name: country.name,\n value: country.iso,\n }))\n .sort((a, b) => {\n if (a.value === selectedCountryCode) return -1;\n if (b.value === selectedCountryCode) return 1;\n return a.name.localeCompare(b.name);\n });\n\n const stateSelectOptions = stateOptions.map((state) => ({\n name: state.name,\n value: state.isoCode,\n }));\n\n useEffect(() => {\n if (isOpen) {\n if (selectedAddress) {\n reset({\n name: selectedAddress.name ?? \"\",\n address1: selectedAddress.address1,\n address2: selectedAddress.address2 ?? \"\",\n city: selectedAddress.city,\n state: selectedAddress.state,\n postal_code: selectedAddress.postal_code,\n phone: selectedAddress.phone ?? \"\",\n country_code: selectedAddress.country_code ?? defaultCountry,\n default: selectedAddress.default,\n });\n handleCountryChange(\n selectedAddress.country_code ?? defaultCountry,\n fetchStates,\n );\n } else {\n reset({\n name: \"\",\n address1: \"\",\n address2: \"\",\n city: \"\",\n state: \"\",\n postal_code: \"\",\n phone: \"\",\n country_code: defaultCountry,\n default: false,\n });\n handleCountryChange(defaultCountry, fetchStates);\n }\n }\n // defaultCountry intentionally omitted: a background addresses refetch can\n // change the parent-computed value while the dialog is open, which would\n // wipe the user's in-progress edits.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedAddress, isOpen, reset, handleCountryChange, fetchStates]);\n\n useEffect(() => {\n handleCountryChange(selectedCountryCode, fetchStates);\n }, [selectedCountryCode, handleCountryChange, fetchStates]);\n\n const handleFormSubmit = handleSubmit((data) => {\n clearErrors(STATIC_FIELD_ORDER);\n const missingRequiredFields = fieldOrder.filter(\n (fieldName) =>\n isFieldRequired(bffFields, fieldName) && isBlank(data[fieldName]),\n );\n\n if (missingRequiredFields.length > 0) {\n for (const fieldName of missingRequiredFields) {\n setError(fieldName, {\n type: \"manual\",\n message: REQUIRED_FIELD_MESSAGES[fieldName],\n });\n }\n return;\n }\n\n const city = bffFields && !fieldOrder.includes(\"city\") ? \"\" : data.city;\n const state = bffFields && !fieldOrder.includes(\"state\") ? \"\" : data.state;\n\n onSubmit({\n address: {\n name: data.name,\n address1: data.address1,\n address2: data.address2 ?? null,\n city,\n state,\n postal_code: data.postal_code,\n phone: data.phone || null,\n country_code: data.country_code,\n default: data.default,\n },\n });\n });\n\n const renderField = (\n fieldName: AddressFieldName,\n containerClassName?: string,\n ): JSX.Element => {\n const label = getFieldLabel(fieldName);\n\n if (fieldName === \"state\" && stateSelectOptions.length > 0) {\n return (\n <FormComboboxField\n key={fieldName}\n control={control}\n name=\"state\"\n label={label}\n placeholder={t(\"select_state\")}\n options={stateSelectOptions}\n containerClassName={containerClassName}\n disabled={isSubmitting}\n />\n );\n }\n\n if (fieldName === \"address1\" && renderAddressAutocomplete) {\n return (\n <div key={fieldName} className={cn(\"space-y-1\", containerClassName)}>\n <Label\n htmlFor={fieldName}\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {label}\n </Label>\n {renderAddressAutocomplete({\n control,\n setValue,\n countryCode: selectedCountryCode,\n stateOptions,\n })}\n </div>\n );\n }\n\n if (fieldName === \"phone\") {\n return (\n <div key={fieldName} className={containerClassName}>\n <Controller\n control={control}\n name=\"phone\"\n render={({ field: { value, onChange }, fieldState: { error } }) => (\n <div className=\"space-y-1\">\n <Label\n htmlFor={phoneInputId}\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {label}\n </Label>\n <PhoneInput\n id={phoneInputId}\n value={value ?? \"\"}\n onChange={onChange}\n defaultCountry={selectedCountryCode}\n error={!!error}\n disabled={isSubmitting}\n searchPlaceholder={t(\"search_country\")}\n emptyText={t(\"no_results_found\")}\n />\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </div>\n )}\n />\n </div>\n );\n }\n\n return (\n <FormTextField\n key={fieldName}\n control={control}\n name={fieldName}\n label={label}\n containerClassName={containerClassName}\n disabled={isSubmitting}\n autoComplete={fieldName === \"name\" ? \"name\" : undefined}\n />\n );\n };\n\n const isEditMode = selectedAddress !== null;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent\n aria-describedby={undefined}\n className=\"max-h-[90vh] max-w-md overflow-y-auto rounded md:max-w-xl\"\n >\n <DialogHeader>\n <DialogTitle className=\"text-lg font-medium\">\n {isEditMode ? t(\"edit_address\") : t(\"add_an_address\")}\n </DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-3 pt-2\">\n <FormComboboxField\n control={control}\n name=\"country_code\"\n label={t(\"country\")}\n placeholder={t(\"select_an_option\")}\n options={countrySelectOptions}\n disabled={isSubmitting}\n />\n {fieldGroups.map((group) => {\n if (group.length === 1) {\n return renderField(group[0]!);\n }\n return (\n <div key={group.join(\"-\")} className=\"flex gap-3\">\n {group.map((fieldName) => renderField(fieldName, \"flex-1\"))}\n </div>\n );\n })}\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n {onDelete ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => {\n if (confirmDelete) {\n onDelete();\n } else {\n setConfirmDelete(true);\n }\n }}\n disabled={isSubmitting || isDeleting}\n className=\"text-destructive hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2 className=\"size-4\" />\n {deleteButtonLabel}\n </Button>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-x-2\">\n <input\n type=\"checkbox\"\n id=\"set_as_default_address\"\n checked={isDefault}\n onChange={() => setValue(\"default\", !isDefault)}\n disabled={isSubmitting || isDeleting}\n className=\"size-4\"\n />\n <label htmlFor=\"set_as_default_address\" className=\"text-sm\">\n {t(\"set_as_default_address\")}\n </label>\n </div>\n\n <Button\n type=\"submit\"\n onClick={handleFormSubmit}\n disabled={isSubmitting || isDeleting}\n >\n {isSubmitting && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isSubmitting ? t(\"saving\") : t(\"save_address\")}\n </Button>\n </div>\n </div>\n\n {error && <div className=\"text-destructive mt-2 text-sm\">{error}</div>}\n </DialogContent>\n </Dialog>\n );\n}\n","import { useEffect, useRef, useState, useCallback } from \"react\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\n\nexport type VgsFieldState = {\n isFocused: boolean;\n isValid: boolean;\n isEmpty: boolean;\n cardType?: string;\n bin?: string;\n last4?: string;\n};\n\nexport type VgsFormState = {\n [key: string]: VgsFieldState;\n};\n\ninterface VGSCollectForm {\n setRouteId: (routeId: string) => void;\n field: (selector: string, config: Record<string, unknown>) => void;\n submit: (\n path: string,\n options: { headers?: Record<string, string> },\n successCallback: (\n status: number,\n response: fluidPay.VgsCollectSubmitResponse,\n ) => void,\n errorCallback: (errors: Record<string, string[]>) => void,\n ) => void;\n unmount: () => void;\n}\n\ninterface VGSCollectLibrary {\n create: (\n vaultId: string,\n environment: string,\n stateCallback: (state: Record<string, Partial<VgsFieldState>>) => void,\n ) => VGSCollectForm;\n}\n\nconst getVGSCollect = (): VGSCollectLibrary | null => {\n if (typeof window !== \"undefined\") {\n return (\n (window as unknown as { VGSCollect?: VGSCollectLibrary }).VGSCollect ||\n null\n );\n }\n return null;\n};\n\nconst DEFAULT_FIELD_STATE: VgsFormState = {\n card_number: { isFocused: false, isValid: false, isEmpty: true },\n card_exp: { isFocused: false, isValid: false, isEmpty: true },\n card_cvc: { isFocused: false, isValid: false, isEmpty: true },\n card_holder: { isFocused: false, isValid: false, isEmpty: true },\n};\n\nexport function useVgsCollect() {\n const [isFormReady, setIsFormReady] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const [isScriptLoaded, setIsScriptLoaded] = useState(false);\n const [scriptLoadError, setScriptLoadError] = useState<string | undefined>(\n undefined,\n );\n\n const [vgsFieldState, setVgsFieldState] =\n useState<VgsFormState>(DEFAULT_FIELD_STATE);\n\n const formRef = useRef<VGSCollectForm | null>(null);\n const onSuccessRef = useRef<\n ((response: fluidPay.VgsCollectSubmitResponse) => void) | null\n >(null);\n const onErrorRef = useRef<((error: string) => void) | null>(null);\n\n // Load VGS Collect script\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n const vgsCollect = getVGSCollect();\n if (vgsCollect) {\n const timeoutId = window.setTimeout(() => {\n setIsScriptLoaded(true);\n }, 0);\n return () => window.clearTimeout(timeoutId);\n }\n\n if (isScriptLoaded) return;\n\n const script = document.createElement(\"script\");\n script.src =\n \"https://js.verygoodvault.com/vgs-collect/3.3.0/vgs-collect.js\";\n script.async = true;\n script.onload = () => setIsScriptLoaded(true);\n // Surface load failures both to a registered consumer (if any) and to\n // state, so a consumer mounting after the failure still sees it.\n script.onerror = () => {\n const message = \"Failed to load payment form\";\n setScriptLoadError(message);\n onErrorRef.current?.(message);\n };\n document.head.appendChild(script);\n }, [isScriptLoaded]);\n\n const updateFieldState = useCallback(\n (state: Record<string, Partial<VgsFieldState>>) => {\n if (!state) return;\n\n setVgsFieldState((prev) => {\n const newState: VgsFormState = { ...prev };\n let hasChanges = false;\n\n Object.keys(state).forEach((key) => {\n const field = state[key];\n if (!field) return;\n\n const next: VgsFieldState = {\n isFocused: !!field.isFocused,\n isValid: !!field.isValid,\n isEmpty: !!field.isEmpty,\n cardType: field.cardType,\n bin: field.bin,\n last4: field.last4,\n };\n\n if (\n !newState[key] ||\n JSON.stringify(newState[key]) !== JSON.stringify(next)\n ) {\n newState[key] = next;\n hasChanges = true;\n }\n });\n\n return hasChanges ? newState : prev;\n });\n },\n [],\n );\n\n const resetVgs = useCallback(() => {\n if (formRef.current) {\n try {\n formRef.current.unmount();\n } catch (e) {\n console.warn(\"VGS unmount error\", e);\n }\n formRef.current = null;\n }\n\n setIsFormReady(false);\n setVgsFieldState({ ...DEFAULT_FIELD_STATE });\n }, []);\n\n const initializeForm = useCallback(\n async (credentials: fluidPay.VaultResponse) => {\n if (!isScriptLoaded) return;\n\n const VGSCollect = getVGSCollect();\n if (!VGSCollect) {\n onErrorRef.current?.(\"Payment script not loaded\");\n return;\n }\n\n try {\n setIsLoading(true);\n\n const vgsEnvironment =\n credentials.vault.environment === \"sandbox\" ? \"sandbox\" : \"live-eu-1\";\n\n const form = VGSCollect.create(\n credentials.vault.vault_id,\n vgsEnvironment,\n (state) => updateFieldState(state),\n );\n\n if (credentials.vault.route_id) {\n form.setRouteId(credentials.vault.route_id);\n }\n\n formRef.current = form;\n\n // Wait for DOM elements\n let retries = 0;\n const maxRetries = 20;\n const retryDelay = 200;\n\n const waitForDom = async (): Promise<boolean> => {\n const allExist = [\n \"vgs-card-number\",\n \"vgs-expiration-date\",\n \"vgs-cvc\",\n \"vgs-card-holder\",\n ].every((id) => {\n const el = document.getElementById(id);\n return el && el.isConnected;\n });\n\n if (allExist) return true;\n if (retries >= maxRetries) return false;\n\n retries++;\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n return waitForDom();\n };\n\n const domReady = await waitForDom();\n if (!domReady) {\n throw new Error(\"Payment form elements not ready\");\n }\n\n // Resolve theme tokens from the wrapper element. The theme engine\n // applies `data-theme` on a subtree element (not <html>), so CSS\n // variables aren't visible on document.documentElement — only on a\n // node inside the themed subtree. Cross-origin iframe contents\n // can't see parent CSS variables, so we resolve concrete values\n // here and pass them to VGS.\n //\n // The `vgs-card-number` element is guaranteed to exist at this\n // point: the `waitForDom` check above blocks until all four VGS\n // wrapper elements are mounted. The hardcoded fallback colors only\n // activate if the CSS variables themselves are unset.\n const themedNode = document.getElementById(\"vgs-card-number\");\n const probeStyle = themedNode ? getComputedStyle(themedNode) : null;\n const foregroundColor =\n probeStyle?.getPropertyValue(\"--color-foreground\").trim() ||\n \"#0a0a0a\";\n const mutedForegroundColor =\n probeStyle?.getPropertyValue(\"--color-muted-foreground\").trim() ||\n \"#737373\";\n\n const css = {\n fontSize: \"16px\",\n fontFamily:\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\",\n color: foregroundColor,\n background: \"transparent\",\n paddingTop: \"0px\",\n paddingBottom: \"0px\",\n paddingLeft: \"0px\",\n paddingRight: \"0px\",\n boxSizing: \"border-box\",\n lineHeight: \"26px\",\n height: \"100%\",\n width: \"100%\",\n \"&::placeholder\": { color: mutedForegroundColor, opacity: 1 },\n \"&:focus\": { outline: \"none\" },\n };\n\n form.field(\"#vgs-card-number\", {\n type: \"card-number\",\n name: \"card_number\",\n placeholder: \"0000 0000 0000 0000\",\n showCardIcon: true,\n css: { ...css, paddingRight: \"40px\" },\n autoComplete: \"cc-number\",\n validations: [\"required\", \"validCardNumber\"],\n });\n\n form.field(\"#vgs-expiration-date\", {\n type: \"card-expiration-date\",\n name: \"card_exp\",\n yearLength: 2,\n placeholder: \"MM / YY\",\n css,\n autoComplete: \"cc-exp\",\n validations: [\"required\", \"validCardExpirationDate\"],\n });\n\n form.field(\"#vgs-cvc\", {\n type: \"card-security-code\",\n name: \"card_cvc\",\n placeholder: \"123\",\n css,\n autoComplete: \"cc-csc\",\n validations: [\"required\", \"validCardSecurityCode\"],\n });\n\n form.field(\"#vgs-card-holder\", {\n type: \"text\",\n name: \"card_holder\",\n placeholder: \"Full Name\",\n css,\n autoComplete: \"cc-name\",\n validations: [\"required\"],\n });\n\n setIsFormReady(true);\n } catch (error) {\n console.error(\"Failed to initialize VGS:\", error);\n onErrorRef.current?.(\"Failed to initialize payment form\");\n } finally {\n setIsLoading(false);\n }\n },\n [isScriptLoaded, updateFieldState],\n );\n\n const submitCard = useCallback(() => {\n if (!formRef.current) {\n onErrorRef.current?.(\"Form not initialized\");\n return;\n }\n\n formRef.current.submit(\n `/post`,\n {},\n (status: number, response: fluidPay.VgsCollectSubmitResponse) => {\n if (status >= 200 && status < 400) {\n onSuccessRef.current?.(response);\n } else {\n onErrorRef.current?.(\"Payment failed\");\n }\n },\n (errors: Record<string, string[]>) => {\n const allErrors = Object.values(errors)\n .flat()\n .filter(\n (msg) =>\n typeof msg === \"string\" &&\n msg.trim().length > 0 &&\n msg !== \"Required\",\n );\n\n const uniqueErrors = Array.from(new Set(allErrors));\n\n let errorMsg = \"Please verify your payment details.\";\n if (uniqueErrors.length > 0) {\n errorMsg = uniqueErrors.join(\", \");\n } else {\n const rawErrors = Object.values(errors).flat();\n if (rawErrors.length > 0) {\n errorMsg = \"Please fill in all required fields.\";\n }\n }\n\n onErrorRef.current?.(errorMsg);\n },\n );\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (formRef.current) {\n try {\n formRef.current.unmount();\n } catch (e) {\n console.warn(\"VGS unmount error\", e);\n }\n formRef.current = null;\n }\n };\n }, []);\n\n return {\n isFormReady,\n isLoading,\n isScriptLoaded,\n scriptLoadError,\n initializeForm,\n submitCard,\n vgsFieldState,\n resetVgs,\n onSuccessRef,\n onErrorRef,\n };\n}\n","import {\n type JSX,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { Controller } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Button,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n cn,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport { useFluidPayApi } from \"@fluid-app/fluid-pay-core\";\nimport {\n useVgsCollect,\n type VgsFieldState,\n type VgsFormState,\n} from \"../hooks/use-vgs-collect\";\n\nconst SENTINEL_CREATE_NEW = \"__create_new__\";\n\nconst formSchema = z.object({\n billing_address_selection: z\n .string()\n .refine((value) => value !== \"\" && value !== SENTINEL_CREATE_NEW, {\n message: \"Please select a billing address\",\n }),\n set_as_default: z.boolean(),\n});\n\ntype FormData = z.infer<typeof formSchema>;\n\nexport interface CreditCardFormSubmitData {\n set_as_default: boolean;\n payment_method: {\n token: string;\n cvv_token: string;\n exp_month: string;\n exp_year: string;\n card_holder: string;\n bin?: string;\n last_four?: string;\n card_network?: string;\n };\n billing_address_id: number;\n}\n\nexport interface RenderAddressFormDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onAddressCreated: (address: fluidPay.CustomerAddress) => void;\n}\n\ninterface CreditCardFormDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: CreditCardFormSubmitData) => void;\n isSubmitting: boolean;\n error?: string;\n jwt: string;\n /** When provided, adds a top \"Same as shipping\" option referencing this address. */\n shippingAddress?: fluidPay.CustomerAddress | null;\n /** Saved customer addresses populating the dropdown. */\n savedAddresses?: fluidPay.CustomerAddress[];\n /** ID of the customer's preferred billing address; used to pre-select. */\n defaultBillingAddressId?: number;\n /**\n * Render-prop slot for AddressFormDialog. The host wires the dialog so this\n * component doesn't import it directly, keeping the address-create flow's\n * data plumbing (mutations, error toasts) under host control.\n */\n renderAddressFormDialog?: (props: RenderAddressFormDialogProps) => ReactNode;\n t: (key: string) => string;\n}\n\nfunction VgsInputWrapper({\n id,\n label,\n fieldState,\n className,\n}: {\n id: string;\n label: string;\n fieldState?: VgsFieldState;\n className?: string;\n}) {\n const isInvalid =\n fieldState?.isValid === false &&\n !fieldState?.isFocused &&\n !fieldState?.isEmpty;\n const isFocused = !!fieldState?.isFocused;\n\n return (\n <div\n id={id}\n role=\"group\"\n aria-label={label}\n data-invalid={isInvalid ? \"\" : undefined}\n className={cn(\n \"border-input selection:bg-primary selection:text-primary-foreground file:text-foreground placeholder:text-muted-foreground h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n \"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n \"data-[invalid]:border-destructive data-[invalid]:ring-destructive/20\",\n \"[&_iframe]:block! [&_iframe]:h-full! [&_iframe]:w-full! [&_iframe]:border-0!\",\n isFocused && \"border-ring ring-ring/50 ring-[3px]\",\n isFocused && isInvalid && \"border-destructive ring-destructive/20\",\n className,\n )}\n />\n );\n}\n\nfunction VgsCardForm({\n vgsFieldState,\n networkError,\n}: {\n vgsFieldState: VgsFormState;\n networkError?: string;\n}) {\n return (\n <div className=\"space-y-4\" aria-label=\"Credit card form\">\n <VgsInputWrapper\n id=\"vgs-card-number\"\n label=\"Card number\"\n fieldState={vgsFieldState[\"card_number\"]}\n />\n\n <div className=\"flex gap-3\">\n <div className=\"flex-1\">\n <VgsInputWrapper\n id=\"vgs-expiration-date\"\n label=\"Expiration date\"\n fieldState={vgsFieldState[\"card_exp\"]}\n />\n </div>\n <div className=\"flex-1\">\n <VgsInputWrapper\n id=\"vgs-cvc\"\n label=\"CVC\"\n fieldState={vgsFieldState[\"card_cvc\"]}\n />\n </div>\n </div>\n\n <VgsInputWrapper\n id=\"vgs-card-holder\"\n label=\"Cardholder name\"\n fieldState={vgsFieldState[\"card_holder\"]}\n />\n\n {networkError && networkError.trim().length > 0 && (\n <div className=\"bg-destructive/10 text-destructive rounded-md p-3 text-sm\">\n {networkError}\n </div>\n )}\n </div>\n );\n}\n\nfunction formatAddressOneLine(addr: fluidPay.CustomerAddress): string {\n const cityStateZip = [addr.city, addr.state, addr.postal_code]\n .filter((p): p is string => Boolean(p && p.trim().length > 0))\n .join(\", \");\n const parts = [\n addr.address1,\n addr.address2,\n cityStateZip,\n addr.country_code,\n ].filter((p): p is string => Boolean(p && p.trim().length > 0));\n return parts.join(\" • \");\n}\n\nfunction AddressSummaryCard({\n address,\n}: {\n address: fluidPay.CustomerAddress;\n}) {\n return (\n <div className=\"border-input bg-muted/30 rounded-md border p-3 text-sm break-words\">\n {address.name && <div className=\"font-medium\">{address.name}</div>}\n <div className=\"text-muted-foreground\">{address.address1}</div>\n {address.address2 && (\n <div className=\"text-muted-foreground\">{address.address2}</div>\n )}\n <div className=\"text-muted-foreground\">\n {[address.city, address.state].filter(Boolean).join(\", \")}\n {address.postal_code ? ` ${address.postal_code}` : \"\"}\n </div>\n {address.country_code && (\n <div className=\"text-muted-foreground\">{address.country_code}</div>\n )}\n </div>\n );\n}\n\nexport default function CreditCardFormDialog({\n isOpen,\n onClose,\n onSubmit,\n isSubmitting,\n error,\n jwt,\n shippingAddress,\n savedAddresses = [],\n defaultBillingAddressId,\n renderAddressFormDialog,\n t,\n}: CreditCardFormDialogProps): JSX.Element {\n const api = useFluidPayApi();\n const [cardError, setCardError] = useState<string | undefined>(undefined);\n const [isVgsSubmitting, setIsVgsSubmitting] = useState(false);\n const [createdAddresses, setCreatedAddresses] = useState<\n fluidPay.CustomerAddress[]\n >([]);\n const [isAddressDialogOpen, setIsAddressDialogOpen] = useState(false);\n\n const {\n isFormReady,\n isLoading: isVgsLoading,\n isScriptLoaded,\n scriptLoadError,\n initializeForm,\n submitCard,\n vgsFieldState,\n resetVgs,\n onSuccessRef,\n onErrorRef,\n } = useVgsCollect();\n\n const shippingId = shippingAddress?.id ?? null;\n const dedupedSavedAddresses = useMemo(\n () =>\n shippingId === null\n ? savedAddresses\n : savedAddresses.filter((a) => a.id !== shippingId),\n [savedAddresses, shippingId],\n );\n\n const allAddresses = useMemo<fluidPay.CustomerAddress[]>(() => {\n const list: fluidPay.CustomerAddress[] = [];\n if (shippingAddress) list.push(shippingAddress);\n list.push(...dedupedSavedAddresses);\n list.push(...createdAddresses);\n return list;\n }, [shippingAddress, dedupedSavedAddresses, createdAddresses]);\n\n const hasRealAddresses = allAddresses.length > 0;\n\n // Default selection per ticket: shipping → default billing → first saved → \"use different\".\n const initialSelection = useMemo<string>(() => {\n if (shippingAddress) return shippingAddress.id.toString();\n if (\n defaultBillingAddressId &&\n dedupedSavedAddresses.some((a) => a.id === defaultBillingAddressId)\n ) {\n return defaultBillingAddressId.toString();\n }\n const first = dedupedSavedAddresses[0];\n if (first) return first.id.toString();\n return SENTINEL_CREATE_NEW;\n }, [shippingAddress, defaultBillingAddressId, dedupedSavedAddresses]);\n\n const { control, handleSubmit, reset, setValue, watch } =\n useZodForm<FormData>(formSchema, {\n defaultValues: {\n billing_address_selection: initialSelection,\n set_as_default: false,\n },\n });\n\n // Reset the form whenever the dialog (re)opens or the upstream address list\n // changes — the initial selection depends on shipping/saved/default props.\n useEffect(() => {\n if (isOpen) {\n reset({\n billing_address_selection: initialSelection,\n set_as_default: false,\n });\n setCreatedAddresses([]);\n }\n }, [isOpen, initialSelection, reset]);\n\n const currentSelection = watch(\"billing_address_selection\");\n const setAsDefault = watch(\"set_as_default\");\n\n const selectedAddress = useMemo(() => {\n if (!currentSelection || currentSelection === SENTINEL_CREATE_NEW) {\n return null;\n }\n const id = Number(currentSelection);\n if (!Number.isFinite(id)) return null;\n return allAddresses.find((a) => a.id === id) ?? null;\n }, [currentSelection, allAddresses]);\n\n const selectOptions = useMemo(() => {\n const options: { name: string; value: string }[] = [];\n if (shippingAddress) {\n options.push({\n name: t(\"same_as_shipping\"),\n value: shippingAddress.id.toString(),\n });\n }\n for (const addr of dedupedSavedAddresses) {\n options.push({\n name: formatAddressOneLine(addr),\n value: addr.id.toString(),\n });\n }\n for (const addr of createdAddresses) {\n options.push({\n name: formatAddressOneLine(addr),\n value: addr.id.toString(),\n });\n }\n options.push({\n name: t(\"use_a_different_address\"),\n value: SENTINEL_CREATE_NEW,\n });\n return options;\n }, [shippingAddress, dedupedSavedAddresses, createdAddresses, t]);\n\n const handleSelectionChange = useCallback(\n (value: string) => {\n if (value === SENTINEL_CREATE_NEW) {\n // Don't commit the sentinel to form state — opening the sub-modal is\n // the action. The form's previous selection remains intact so closing\n // the sub-modal without saving leaves the dropdown unchanged.\n setIsAddressDialogOpen(true);\n return;\n }\n setValue(\"billing_address_selection\", value, { shouldValidate: true });\n },\n [setValue],\n );\n\n const handleAddressCreated = useCallback(\n (address: fluidPay.CustomerAddress) => {\n setCreatedAddresses((list) => [...list, address]);\n setValue(\"billing_address_selection\", address.id.toString(), {\n shouldValidate: true,\n });\n setIsAddressDialogOpen(false);\n },\n [setValue],\n );\n\n const handleVgsSuccess = useCallback(\n (response: fluidPay.VgsCollectSubmitResponse) => {\n const expValue = response?.card_exp;\n if (!expValue) {\n setCardError(\"Missing expiration date from payment form\");\n setIsVgsSubmitting(false);\n return;\n }\n\n const cleanExp = expValue.replace(/\\s+/g, \"\");\n const [expMonth, expYearShort] = cleanExp.split(\"/\");\n\n if (!expMonth || !expYearShort) {\n setCardError(\"Invalid expiration date format\");\n setIsVgsSubmitting(false);\n return;\n }\n\n const expYear =\n expYearShort.length === 2 ? `20${expYearShort}` : expYearShort;\n\n const cardholderName = response?.card_holder?.trim();\n if (!cardholderName) {\n setCardError(\"Cardholder name is required\");\n setIsVgsSubmitting(false);\n return;\n }\n\n handleSubmit(\n (formData) => {\n const billingAddressId = Number(formData.billing_address_selection);\n if (!Number.isFinite(billingAddressId)) {\n setCardError(\"Please select a billing address.\");\n setIsVgsSubmitting(false);\n return;\n }\n\n const cardMeta = vgsFieldState[\"card_number\"];\n onSubmit({\n set_as_default: formData.set_as_default,\n payment_method: {\n token: response.card_number,\n cvv_token: response.card_cvc,\n exp_month: expMonth,\n exp_year: expYear,\n card_holder: cardholderName,\n ...(cardMeta?.bin ? { bin: cardMeta.bin } : {}),\n ...(cardMeta?.last4 ? { last_four: cardMeta.last4 } : {}),\n ...(cardMeta?.cardType\n ? { card_network: cardMeta.cardType }\n : {}),\n },\n billing_address_id: billingAddressId,\n });\n setIsVgsSubmitting(false);\n },\n () => {\n setCardError(\"Please select a billing address.\");\n setIsVgsSubmitting(false);\n },\n )();\n },\n [handleSubmit, onSubmit, vgsFieldState],\n );\n\n const handleVgsError = useCallback((err: string) => {\n setCardError(err);\n setIsVgsSubmitting(false);\n }, []);\n\n useEffect(() => {\n onSuccessRef.current = handleVgsSuccess;\n onErrorRef.current = handleVgsError;\n }, [handleVgsSuccess, handleVgsError, onSuccessRef, onErrorRef]);\n\n useEffect(() => {\n if (!isOpen || !isScriptLoaded) return;\n\n let cancelled = false;\n\n const init = async () => {\n try {\n const credentials = await api.vault.fetchCredentials(jwt);\n if (!cancelled) {\n await initializeForm(credentials);\n }\n } catch (err) {\n console.error(\"Failed to fetch vault credentials:\", err);\n if (!cancelled) {\n setCardError(\"Failed to initialize payment form\");\n }\n }\n };\n\n init();\n\n return () => {\n cancelled = true;\n };\n }, [isOpen, isScriptLoaded, jwt, api, initializeForm]);\n\n const handleClose = () => {\n resetVgs();\n reset();\n setCardError(undefined);\n setIsVgsSubmitting(false);\n setIsAddressDialogOpen(false);\n onClose();\n };\n\n const handleFormSubmit = handleSubmit(() => {\n setIsVgsSubmitting(true);\n setCardError(undefined);\n submitCard();\n });\n\n const isBusy = isSubmitting || isVgsSubmitting;\n\n return (\n <>\n <Dialog open={isOpen} onOpenChange={(open) => !open && handleClose()}>\n <DialogContent className=\"max-h-[90vh] max-w-md overflow-y-auto p-0 md:max-w-xl\">\n <div className=\"relative min-w-0 p-6\">\n {(isVgsLoading || !isFormReady) && !scriptLoadError && (\n <div className=\"bg-background/80 absolute inset-0 z-10 flex items-center justify-center rounded backdrop-blur-[1px]\">\n <div className=\"flex items-center justify-center\">\n <div className=\"border-primary mr-3 size-5 animate-spin rounded-full border-2 border-t-2 border-t-transparent\" />\n <p className=\"text-muted-foreground text-sm font-medium\">\n {t(\"securing\")}\n </p>\n </div>\n </div>\n )}\n <DialogHeader>\n <DialogTitle>{t(\"add_credit_card\")}</DialogTitle>\n </DialogHeader>\n\n <div className=\"space-y-4 pt-6\">\n <VgsCardForm\n vgsFieldState={vgsFieldState}\n networkError={cardError ?? scriptLoadError ?? error}\n />\n\n <div className=\"space-y-3 pt-2\">\n <h3 className=\"text-muted-foreground text-sm font-medium\">\n {t(\"billing_address\")}\n </h3>\n {hasRealAddresses ? (\n <Controller\n control={control}\n name=\"billing_address_selection\"\n render={({\n field: { value },\n fieldState: { error: fieldError },\n }) => (\n <div className=\"space-y-1\">\n <Label\n htmlFor=\"billing-address-selection\"\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {t(\"billing_address\")}\n </Label>\n <Select\n value={value}\n onValueChange={handleSelectionChange}\n disabled={isBusy}\n >\n <SelectTrigger\n id=\"billing-address-selection\"\n aria-invalid={!!fieldError}\n className=\"w-full min-w-0 [&>span]:block! [&>span]:min-w-0 [&>span]:truncate\"\n >\n <SelectValue placeholder={t(\"select_an_option\")} />\n </SelectTrigger>\n <SelectContent>\n {selectOptions.map((opt) => (\n <SelectItem\n key={opt.value}\n value={opt.value}\n // Radix Select fires onValueChange only when the value\n // *changes*. Picking the already-selected sentinel\n // would otherwise be a no-op, leaving the user with\n // no way to reopen the address form.\n onClick={\n opt.value === SENTINEL_CREATE_NEW\n ? () => setIsAddressDialogOpen(true)\n : undefined\n }\n >\n {opt.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {fieldError?.message && (\n <p className=\"text-destructive text-sm\">\n {fieldError.message}\n </p>\n )}\n </div>\n )}\n />\n ) : (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setIsAddressDialogOpen(true)}\n disabled={isBusy}\n className=\"w-full\"\n >\n {t(\"add_billing_address\")}\n </Button>\n )}\n\n {selectedAddress && (\n <AddressSummaryCard address={selectedAddress} />\n )}\n </div>\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-x-2\">\n <input\n type=\"checkbox\"\n id=\"set_as_default_cc\"\n checked={setAsDefault}\n onChange={() => setValue(\"set_as_default\", !setAsDefault)}\n disabled={isBusy}\n className=\"size-4\"\n />\n <label htmlFor=\"set_as_default_cc\" className=\"text-sm\">\n {t(\"set_as_default_payment_method\")}\n </label>\n </div>\n\n <Button\n type=\"button\"\n onClick={handleFormSubmit}\n disabled={isBusy}\n >\n {isBusy && (\n <div className=\"mr-2 size-4 animate-spin rounded-full border-2 border-current/30 border-t-current\" />\n )}\n {isBusy ? t(\"saving\") : t(\"save_card\")}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {renderAddressFormDialog?.({\n isOpen: isAddressDialogOpen,\n onClose: () => setIsAddressDialogOpen(false),\n onAddressCreated: handleAddressCreated,\n })}\n </>\n );\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\nimport { Lock } from \"lucide-react\";\n\nimport { Dialog, DialogContent, DialogTitle } from \"@fluid-app/ui-primitives\";\n\n/** Display strings for the modal chrome — injected by the host app so the\n * package stays translation-system agnostic. */\nexport interface VgsThreeDSModalLabels {\n title: string;\n cancel: string;\n timedOut: string;\n stillWorking: string;\n dontRefresh: string;\n}\n\nexport interface VgsThreeDSModalProps {\n isOpen: boolean;\n challengeHtml: string;\n challengeUrl?: string;\n onClose: (error?: string) => void;\n labels: VgsThreeDSModalLabels;\n}\n\nfunction escapeAttr(s: string | null): string {\n return (s ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n/**\n * Parse the challenge HTML to extract the form action URL and hidden fields.\n * This lets us POST directly to the bank ACS, bypassing intermediary pages\n * (e.g. Entersekt's red.js auto-submit) that cause extra iframe loads.\n */\nfunction parseDirectPostHtml(\n challengeHtml: string,\n challengeUrl?: string,\n): string | null {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(challengeHtml, \"text/html\");\n const form = doc.querySelector(\"form\");\n if (!form) return null;\n\n const action = challengeUrl || form.getAttribute(\"action\");\n if (!action) return null;\n\n const hiddenInputs = form.querySelectorAll('input[type=\"hidden\"]');\n if (hiddenInputs.length === 0) return null;\n\n const fieldsHtml = Array.from(hiddenInputs)\n .map(\n (input) =>\n `<input type=\"hidden\" name=\"${escapeAttr(input.getAttribute(\"name\"))}\" value=\"${escapeAttr(input.getAttribute(\"value\"))}\">`,\n )\n .join(\"\\n\");\n\n return `<!DOCTYPE html>\n<html>\n<body>\n<form id=\"acsForm\" method=\"POST\" action=\"${escapeAttr(action)}\">\n${fieldsHtml}\n</form>\n<script>document.getElementById(\"acsForm\").submit();</script>\n</body>\n</html>`;\n } catch {\n return null;\n }\n}\n\n/**\n * Pure iframe container for the 3DS challenge.\n *\n * The modal does NOT try to detect challenge completion via load-counting or\n * postMessage — that was fragile across different bank ACS implementations.\n * Instead, `useVgs3DSCompletion` subscribes to the realtime transport as soon\n * as the modal opens and receives the authoritative result from the backend.\n *\n * This modal only handles:\n * - Rendering the ACS challenge iframe\n * - User cancellation (Cancel button in header band)\n * - Hard timeout (6.5 min) as a safety net\n */\nexport function VgsThreeDSModal({\n isOpen,\n challengeHtml,\n challengeUrl,\n onClose,\n labels,\n}: VgsThreeDSModalProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const onCloseRef = useRef(onClose);\n // Capture labels in a ref so the timeout effect can read the current\n // translation without depending on labels in its deps array (host apps\n // typically build the labels object inline per render and would otherwise\n // reset the timer on every re-render).\n const labelsRef = useRef(labels);\n\n useEffect(() => {\n onCloseRef.current = onClose;\n });\n\n useEffect(() => {\n labelsRef.current = labels;\n });\n\n // Build a direct-POST page that skips intermediary auto-redirect pages.\n // Falls back to the original challengeHtml if parsing fails.\n const iframeSrcDoc = useMemo(\n () => parseDirectPostHtml(challengeHtml, challengeUrl) ?? challengeHtml,\n [challengeHtml, challengeUrl],\n );\n\n // Hard timeout — aligned just past the backend safety-net boundary (~6:00)\n // so the cart poll surfaces the backend's verdict (accurate decline,\n // rescued approval, or TIMEOUT) before the modal gives up at 6.5 min.\n // (The old comment attributed the 5-minute value to the EMVCo spec;\n // EMVCo leaves challenge-timeout handling to the ACS/issuer — the real\n // constraint is our own safety-net boundary.) Depends only on isOpen so\n // the timer resets on open, not on content change.\n useEffect(() => {\n if (!isOpen) return;\n\n const timeoutId = setTimeout(() => {\n onCloseRef.current(labelsRef.current.timedOut);\n }, 390_000);\n\n return () => {\n clearTimeout(timeoutId);\n };\n }, [isOpen]);\n\n const handleCancel = () => {\n onClose();\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={handleCancel}>\n {/* z-[110]: a bank challenge must never be occluded by host chrome.\n Card-add surfaces launch this modal from a dialog that pins its\n content at z-[100] (fluid-checkout's credit-card dialog), where the\n default z-50 + DOM-order stacking would paint the host dialog on\n top of the challenge — and Radix would inert it underneath. */}\n <DialogContent\n className=\"z-[110] h-[600px] max-w-2xl p-0 sm:max-w-2xl\"\n overlayClassName=\"z-[110]\"\n showCloseButton={false}\n >\n <div className=\"flex h-full w-full flex-col\">\n {/* Header band — replaces VisuallyHidden DialogTitle */}\n <div className=\"flex items-center justify-between border-b border-gray-200 px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <Lock className=\"h-4 w-4 text-gray-600\" aria-hidden=\"true\" />\n <DialogTitle className=\"text-sm font-semibold text-gray-900\">\n {labels.title}\n </DialogTitle>\n </div>\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded text-xs text-indigo-600 hover:text-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none\"\n >\n {labels.cancel}\n </button>\n </div>\n\n {/* Iframe (unchanged behavior). */}\n <div className=\"relative flex-1\">\n {challengeHtml && (\n <iframe\n ref={iframeRef}\n srcDoc={iframeSrcDoc}\n className=\"h-full w-full border-0\"\n title=\"VGS 3DS Challenge\"\n sandbox=\"allow-same-origin allow-scripts allow-forms allow-top-navigation allow-popups\"\n />\n )}\n </div>\n\n {/* Footer band — indeterminate progress + status caption + tab-refresh hint */}\n <div className=\"border-t border-gray-200 px-4 py-3\">\n <div\n data-testid=\"threeds-progress-bar\"\n role=\"progressbar\"\n aria-label={labels.stillWorking}\n className=\"relative h-1 overflow-hidden rounded-full bg-indigo-100\"\n >\n <div className=\"threeds-progress-pill absolute top-0 h-full w-[30%] rounded-full bg-indigo-500\" />\n </div>\n <div className=\"mt-2 flex items-center justify-between text-xs\">\n <span className=\"text-gray-600\">{labels.stillWorking}</span>\n <span className=\"text-gray-400\">{labels.dontRefresh}</span>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport default VgsThreeDSModal;\n","// Pure attempt-scoping predicate shared by the poll and Pusher paths of\n// useVgs3DSCompletion. Presence-guarded on BOTH sides: when either the\n// active attempt key or the result's key is missing (backend not yet\n// deployed, old event shape), the result is honored — exactly today's\n// unscoped behavior.\nexport function belongsToActiveAttempt(\n activeMerchantTxId: string | undefined,\n resultMerchantTxId: string | undefined,\n): boolean {\n if (!activeMerchantTxId || !resultMerchantTxId) return true;\n return resultMerchantTxId === activeMerchantTxId;\n}\n","import { useEffect, useEffectEvent, useRef } from \"react\";\nimport type { ThreeDSStatus } from \"@fluid-app/fluid-pay-core/types\";\nimport { belongsToActiveAttempt } from \"./three-ds-attempt\";\n\nexport interface Vgs3DSCompletionResult {\n success: boolean;\n transactionId: string;\n status?: string;\n}\n\nexport interface Vgs3DSChallengeEvent {\n xid: string;\n /** Optional for backward compatibility: older events may omit it, and the\n * attempt-scoping guard deliberately honors key-less events. */\n merchant_transaction_id?: string;\n status: \"success\" | \"failed\";\n challenge_status?: string;\n error_message?: string;\n}\n\n/** Minimal structural slice of a realtime channel — pusher-js's `Channel`\n * satisfies this as-is (its `bind`/`unbind` return `Channel`, which is\n * assignable to a `void`-returning method). */\nexport interface ThreeDsRealtimeChannel {\n bind(eventName: string, handler: (data: Vgs3DSChallengeEvent) => void): void;\n unbind(\n eventName: string,\n handler: (data: Vgs3DSChallengeEvent) => void,\n ): void;\n}\n\n/** Transport abstraction for the fast path — the app's `pusherClient`\n * satisfies this structurally with no adapter. */\nexport interface ThreeDsRealtimeTransport {\n subscribe(channelName: string): ThreeDsRealtimeChannel | null;\n unsubscribe(channelName: string): void;\n}\n\ninterface UseVgs3DSCompletionOptions {\n /** Realtime channel carrying the `vgs_3ds_challenge` event\n * (e.g. `private-cart-{cartToken}`). */\n channelName: string;\n transport: ThreeDsRealtimeTransport;\n /** Status poll — the reliable fallback alongside the realtime fast path. */\n checkStatus: () => Promise<ThreeDSStatus | null>;\n onComplete: (result: Vgs3DSCompletionResult) => void;\n onError: (error: string) => void;\n /** Subscribe to the realtime channel and start polling when true (i.e. when the 3DS modal is open). */\n enabled?: boolean;\n /** The active attempt's merchant_tx_id — when present, completion signals\n * for any OTHER attempt are ignored (stale metadata, late webhooks). */\n merchantTxId?: string;\n /** Called when the attempt was canceled (e.g. from another tab) — a quiet,\n * no-error terminal. */\n onCanceled?: () => void;\n}\n\n// Poll interval and timeout for the status-poll fallback.\n// Polling at 3-second intervals is the reliable secondary check alongside\n// the Pusher fast path (the backend broadcasts the result immediately).\n// The 6.5-minute timeout sits past the backend safety-net boundary (~6:00),\n// so the poll surfaces the backend's verdict — accurate decline, rescued\n// approval, or TIMEOUT — instead of the frontend self-declaring first.\nconst POLL_INTERVAL_MS = 3_000;\nconst TIMEOUT_MS = 390_000;\n\n/**\n * Subscribes to the realtime transport and polls the injected status check\n * for the 3DS challenge result.\n *\n * Activated by `enabled` — pass `vgsThreeDSModal.isOpen` so the subscription\n * starts as soon as the challenge modal opens. No URL-param dance needed.\n */\nexport function useVgs3DSCompletion({\n channelName,\n transport,\n checkStatus,\n onComplete,\n onError,\n enabled = false,\n merchantTxId,\n onCanceled,\n}: UseVgs3DSCompletionOptions) {\n const hasHandledRef = useRef(false);\n const isSubscribedRef = useRef(false);\n\n // Kept current via a per-render effect so an inline `transport` value at\n // the call site never retriggers the main effect.\n const transportRef = useRef(transport);\n useEffect(() => {\n transportRef.current = transport;\n });\n\n const handleComplete = useEffectEvent((result: Vgs3DSCompletionResult) => {\n onComplete(result);\n });\n\n const handleError = useEffectEvent((error: string) => {\n onError(error);\n });\n\n const handleCanceled = useEffectEvent(() => {\n onCanceled?.();\n });\n\n // Non-reactive wrapper so an inline `checkStatus` closure at the call site\n // never retriggers the main effect. Called from the poll during the active\n // phase only — never from cleanup.\n const performStatusCheck = useEffectEvent(() => checkStatus());\n\n // Shared handler for Pusher events, polling results, and timeout.\n // hasHandledRef ensures only the first source to resolve wins.\n const handleResult = useEffectEvent(\n (result: {\n status: \"success\" | \"failed\";\n errorMessage?: string;\n transactionId?: string;\n }) => {\n if (hasHandledRef.current) return;\n hasHandledRef.current = true;\n\n if (result.status === \"success\") {\n handleComplete({\n success: true,\n transactionId: result.transactionId ?? \"\",\n status: result.status,\n });\n } else {\n handleError(result.errorMessage || \"3DS verification failed\");\n }\n },\n );\n\n // Reset handled flag when enabled toggles off (modal closed / new attempt).\n useEffect(() => {\n if (!enabled && hasHandledRef.current) {\n hasHandledRef.current = false;\n }\n }, [enabled]);\n\n useEffect(() => {\n if (!enabled || !channelName || hasHandledRef.current) {\n return;\n }\n\n if (isSubscribedRef.current) {\n return;\n }\n\n let cancelled = false;\n // Captured ONCE so subscribe and the cleanup's unsubscribe use the same\n // transport object even if a different one is passed mid-flight.\n const activeTransport = transportRef.current;\n\n isSubscribedRef.current = true;\n const channel = activeTransport.subscribe(channelName);\n const hasPusher = !!channel;\n\n if (!hasPusher) {\n console.error(\n \"[VGS 3DS] Pusher subscribe returned null — using status polling only\",\n );\n isSubscribedRef.current = false;\n // intentionally NOT returning — polling + timeout below are the fallback\n }\n\n // --- Fast path: Pusher event ---\n const handleVgs3DSChallenge = (data: Vgs3DSChallengeEvent) => {\n if (cancelled) return;\n\n // Attempt-scoping: ignore events for a different (older) attempt.\n if (!belongsToActiveAttempt(merchantTxId, data.merchant_transaction_id)) {\n return;\n }\n\n handleResult({\n status: data.status,\n errorMessage: data.error_message,\n transactionId: data.merchant_transaction_id,\n });\n };\n\n if (hasPusher) {\n channel.bind(\"vgs_3ds_challenge\", handleVgs3DSChallenge);\n }\n\n // --- Reliable fallback: poll the injected status check ---\n const pollInterval = setInterval(async () => {\n if (hasHandledRef.current || cancelled) return;\n\n const status = await performStatusCheck();\n if (cancelled || !status) return;\n\n // Attempt-scoping: stale metadata from a previous attempt must not\n // resolve this modal (the backend wipes per-attempt keys on re-init,\n // but a late webhook can still land mid-retry).\n if (!belongsToActiveAttempt(merchantTxId, status.merchantTxId)) {\n return;\n }\n\n if (status.completed) {\n handleResult({ status: \"success\" });\n } else if (status.failed) {\n handleResult({\n status: \"failed\",\n errorMessage:\n status.errorMessage ||\n `3DS verification ${status.status || \"failed\"}`,\n });\n } else if (status.canceled) {\n // Canceled (possibly from another tab): quiet no-error terminal.\n if (!hasHandledRef.current) {\n hasHandledRef.current = true;\n handleCanceled();\n }\n }\n }, POLL_INTERVAL_MS);\n\n // --- Safety net: hard timeout ---\n const timeout = setTimeout(() => {\n if (hasHandledRef.current || cancelled) return;\n\n console.error(\"[VGS 3DS] Timed out waiting for 3DS result\");\n handleResult({\n status: \"failed\",\n errorMessage: \"Payment verification timed out. Please try again.\",\n });\n }, TIMEOUT_MS);\n\n // Cleanup — cancelled flag prevents in-flight polls from calling handleResult\n return () => {\n cancelled = true;\n clearTimeout(timeout);\n clearInterval(pollInterval);\n if (hasPusher) {\n channel.unbind(\"vgs_3ds_challenge\", handleVgs3DSChallenge);\n activeTransport.unsubscribe(channelName);\n }\n isSubscribedRef.current = false;\n };\n }, [enabled, channelName, merchantTxId]);\n}\n","import { z } from \"zod\";\n\nexport const browserInfoSchema = z.object({\n color_depth: z.number().optional(),\n screen_height: z.number().optional(),\n screen_width: z.number().optional(),\n time_zone: z.number().optional(),\n java_enabled: z.boolean().optional(),\n java_script_enabled: z.boolean().optional(),\n language: z.string().optional(),\n user_agent: z.string().optional(),\n accept_header: z.string().optional(),\n});\n\nexport type BrowserInfo = z.infer<typeof browserInfoSchema>;\n\nexport function getBrowserInfo(): BrowserInfo | undefined {\n // Check if we're in a browser environment\n if (typeof window === \"undefined\") {\n console.warn(\"getBrowserInfo called in non-browser environment\");\n return undefined;\n }\n\n try {\n const { screen, navigator } = window;\n\n // Validate that we have the required objects\n if (!screen || !navigator) {\n console.warn(\"Missing screen or navigator objects\");\n return undefined;\n }\n\n const browserInfo: BrowserInfo = {\n color_depth: screen.colorDepth || undefined,\n screen_height: screen.height || undefined,\n screen_width: screen.width || undefined,\n time_zone: new Date().getTimezoneOffset(),\n java_enabled:\n typeof navigator.javaEnabled === \"function\"\n ? navigator.javaEnabled()\n : false,\n java_script_enabled: true,\n language: navigator.language || undefined,\n user_agent: navigator.userAgent || undefined,\n accept_header: \"*/*\",\n };\n\n // Validate that we have at least some essential info\n if (\n !browserInfo.screen_height ||\n !browserInfo.screen_width ||\n !browserInfo.user_agent\n ) {\n console.warn(\"Missing essential browser info fields\");\n }\n\n return browserInfo;\n } catch (error) {\n console.error(\"Error getting browser info:\", error);\n return undefined;\n }\n}\n","/**\n * TypeScript interfaces for portal-tenant pay management.\n * These types mirror the portal-tenant BFF `/api/pay/*` endpoints.\n * Packages import from here instead of from any concrete API client.\n */\n\nimport type { ResponseMeta } from \"./fluidos-api\";\n\n// ---------------------------------------------------------------------------\n// Address\n// ---------------------------------------------------------------------------\n\nexport interface PayAddress {\n id: number;\n name: string | null;\n street1: string;\n street2: string | null;\n city: string;\n state: string;\n zip: string;\n country: string;\n phone?: string | null;\n default: boolean;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface CreateAddressBody {\n address: {\n name?: string | null;\n street1: string;\n street2?: string | null;\n city: string;\n state: string;\n zip: string;\n country: string;\n phone?: string | null;\n default?: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Payment method\n// ---------------------------------------------------------------------------\n\nexport interface PayPaymentMethod {\n id: number;\n type: string;\n brand: string | null;\n last_four: string;\n logo_url: string | null;\n exp_month: number | null;\n exp_year: number | null;\n card_holder: string | null;\n default: boolean;\n billing_address: PayAddress | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport const TOKEN_PROVIDER_VGS = \"vgs\" as const;\n\n// Body for POST /api/pay/payment_methods on the portal-tenant BFF.\n//\n// Note: the BFF always creates the PM with customer scope\n// (PaymentMethodCreator hardcodes set_customer_payment_method: true on its\n// way into the FluidPay action), so PMs created via this body are always\n// immediately referenceable from customer.payment_methods — no need to\n// expose set_customer_payment_method to the client. The checkout app's\n// direct /fluid_pay/create_payment_method route exposes it because that\n// route does not have the same default.\nexport interface CreatePaymentMethodBody {\n payment_method: {\n type: \"card\";\n token: string;\n token_provider?: string;\n exp_month: string;\n exp_year: string;\n cvv_token?: string;\n card_holder?: string;\n default?: boolean;\n bin?: string;\n last_four?: string;\n card_network?: string;\n };\n billing_address_id: number;\n // `| null` mirrors the OpenAPI contract / generated client so callers can send\n // an explicit null instead of being forced to omit the field.\n browser_info?: Record<string, unknown> | null;\n}\n\nexport interface UpdatePaymentMethodBody {\n payment_method: {\n default?: boolean;\n billing_address?: {\n name?: string | null;\n street1: string;\n street2?: string | null;\n city: string;\n state: string;\n zip: string;\n country: string;\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Vault\n// ---------------------------------------------------------------------------\n\nexport interface VaultCredentials {\n vault_id: string;\n environment: string;\n route_id: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Response types\n// ---------------------------------------------------------------------------\n\nexport interface AddressesResponse {\n addresses: PayAddress[];\n meta: ResponseMeta;\n}\n\nexport interface PaymentMethodsResponse {\n payment_methods: PayPaymentMethod[];\n meta: ResponseMeta;\n}\n\nexport interface CreatedPaymentMethodResult {\n payment_method: PayPaymentMethod;\n meta: ResponseMeta;\n}\n\nexport interface ThreeDsRequiredCreateResult {\n three_ds: {\n status: \"three_ds_required\";\n session_token: string;\n tds_method_content: string | null;\n fingerprinting_required: boolean | null;\n merchant_tx_id: string | null;\n };\n meta: ResponseMeta;\n}\n\n// Discriminated union: 201 (created PM) | 200 (3DS required).\nexport type CreatePaymentMethodResponse =\n | CreatedPaymentMethodResult\n | ThreeDsRequiredCreateResult;\n\nexport function isThreeDsRequiredCreate(\n r: CreatePaymentMethodResponse,\n): r is ThreeDsRequiredCreateResult {\n return \"three_ds\" in r && r.three_ds.status === \"three_ds_required\";\n}\n\nexport interface ConfirmApprovedResult {\n status: \"approved\";\n session_token: string;\n payment_method: PayPaymentMethod;\n was_created?: boolean;\n}\n\nexport interface ConfirmChallengeResult {\n status: \"three_ds_challenge\";\n session_token: string;\n challenge_form: string | null;\n challenge_url: string | null;\n}\n\nexport type ConfirmPaymentMethod3DSResponse =\n | ConfirmApprovedResult\n | ConfirmChallengeResult;\n\n// 3DS session lifecycle state from the BFF. The known values are listed for\n// autocomplete + typo-safety, but the type stays open (`string & {}`) because\n// the backend enum may grow: the adapter forwards the wire string without a\n// lying `as` cast, and an unrecognized state stays non-terminal in\n// mapSessionStateToThreeDSStatus — resolved by the EMVCo polling timeout rather\n// than mis-mapped to completed/failed/canceled.\nexport type ThreeDsSessionState =\n | \"initialized\"\n | \"pending\"\n | \"approved\"\n | \"denied\"\n | \"timed_out\"\n | \"cancelled\"\n | \"skipped\"\n | (string & {});\n\nexport interface ThreeDsStatusResponse {\n session_token: string;\n state: ThreeDsSessionState;\n payment_method_id: number | null;\n expired: boolean | null;\n}\n\nexport interface ThreeDsCancelResponse {\n session_token: string;\n state: ThreeDsSessionState;\n cancelled: boolean | null;\n}\n\nexport interface VaultResponse {\n vault: VaultCredentials;\n meta: ResponseMeta;\n}\n\n// ---------------------------------------------------------------------------\n// Points ledger\n// ---------------------------------------------------------------------------\n\nexport interface PointsLedgerEntry {\n id: number;\n amount: number;\n total_balance: number;\n metadata: Record<string, unknown> | null;\n created_at: string;\n}\n\nexport interface PointsLedgersResponse {\n points_ledgers: PointsLedgerEntry[];\n meta: ResponseMeta;\n}\n","import { type FluidPayApi, type fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport type { PayApi } from \"@fluid-app/portal-core/pay-api\";\nimport type { PayPaymentMethod } from \"@fluid-app/portal-core/pay-types\";\n\n/**\n * Maps a portal-tenant PayPaymentMethod to the fluidPay shape consumed by\n * profile-ui components (PaymentMethodDropdown, CreditCardFormDialog, etc.).\n * Co-located with the adapter because both speak the portal-tenant ↔ fluid-pay\n * boundary.\n */\nexport function mapToFluidPayPaymentMethod(\n raw: PayPaymentMethod,\n): fluidPay.CustomerPaymentMethod {\n return {\n id: raw.id,\n created_at: raw.created_at ?? \"\",\n default: raw.default,\n details: {\n card_network: raw.brand,\n card_brand: raw.brand,\n card_holder: raw.card_holder,\n last_four: raw.last_four,\n logo_url: raw.logo_url ?? undefined,\n exp_month: raw.exp_month ?? undefined,\n exp_year: raw.exp_year ?? undefined,\n },\n billing_address: raw.billing_address\n ? {\n name: raw.billing_address.name,\n address1: raw.billing_address.street1,\n address2: raw.billing_address.street2,\n city: raw.billing_address.city,\n state: raw.billing_address.state,\n zip: raw.billing_address.zip,\n country_code: raw.billing_address.country,\n }\n : null,\n payment_type: raw.type,\n source: \"\",\n updated_at: raw.updated_at ?? \"\",\n };\n}\n\nconst notImplemented = () => {\n throw new Error(\"Not available in portal-tenant context\");\n};\n\n/**\n * Adapts the portal-tenant PayApi to the FluidPayApi shape required by\n * FluidPayCoreProvider. Only `vault.fetchCredentials` is implemented —\n * the rest of the FluidPayApi surface is not used by the profile-ui\n * components that consume this provider in the portal context.\n */\nexport function createFluidPayApiAdapter(payApi: PayApi): FluidPayApi {\n return {\n customers: { fetchAccount: notImplemented, update: notImplemented },\n addresses: {\n fetchAll: notImplemented,\n create: notImplemented,\n update: notImplemented,\n delete: notImplemented,\n },\n paymentMethods: {\n fetchAll: notImplemented,\n addCreditCard: notImplemented,\n delete: notImplemented,\n updateDefault: notImplemented,\n update: notImplemented,\n },\n vault: {\n fetchCredentials: async () => {\n const response = await payApi.fetchVaultCredentials();\n return {\n vault: {\n vault_id: response.vault.vault_id,\n environment: response.vault.environment,\n route_id: response.vault.route_id ?? \"\",\n requires_3ds: false,\n },\n meta: {\n request_id: response.meta?.request_id ?? \"\",\n timestamp: response.meta?.timestamp ?? \"\",\n },\n } satisfies fluidPay.VaultResponse;\n },\n },\n };\n}\n","export interface StateOption {\n readonly isoCode: string;\n readonly name: string;\n}\n\ninterface AdministrativeAreaDetails {\n readonly state?: string;\n readonly administrativeAreaShort?: string;\n readonly administrativeAreaLong?: string;\n}\n\nfunction normalize(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction firstPresent(...values: Array<string | undefined>): string {\n return values.find((value) => value && value.trim() !== \"\") ?? \"\";\n}\n\nfunction findStateByIsoCode(\n stateOptions: readonly StateOption[],\n value: string,\n): StateOption | undefined {\n const normalizedValue = normalize(value);\n if (!normalizedValue) return undefined;\n return stateOptions.find(\n (state) => normalize(state.isoCode) === normalizedValue,\n );\n}\n\nfunction findStateByName(\n stateOptions: readonly StateOption[],\n value: string,\n): StateOption | undefined {\n const normalizedValue = normalize(value);\n if (!normalizedValue) return undefined;\n return stateOptions.find(\n (state) => normalize(state.name) === normalizedValue,\n );\n}\n\nexport function resolveAdministrativeAreaState(\n details: AdministrativeAreaDetails,\n stateOptions: readonly StateOption[] = [],\n): string {\n const displayValue = firstPresent(\n details.state,\n details.administrativeAreaLong,\n details.administrativeAreaShort,\n );\n\n if (stateOptions.length === 0) return displayValue;\n\n const candidates = [\n details.administrativeAreaShort,\n details.state,\n details.administrativeAreaLong,\n ].filter((value): value is string => Boolean(value));\n\n for (const candidate of candidates) {\n const state =\n findStateByIsoCode(stateOptions, candidate) ??\n findStateByName(stateOptions, candidate);\n if (state) return state.isoCode;\n }\n\n return displayValue;\n}\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type JSX,\n} from \"react\";\nimport {\n useController,\n type Control,\n type FieldValues,\n type Path,\n type PathValue,\n type UseFormSetValue,\n} from \"react-hook-form\";\nimport { Input, Label } from \"@fluid-app/ui-primitives\";\nimport { useAddressAutocompleteApi } from \"../api-context\";\nimport type { AddressDetails, AddressSuggestion } from \"../api\";\nimport {\n resolveAdministrativeAreaState,\n type StateOption,\n} from \"../administrative-area\";\n\nexport interface AddressAutocompleteInputProps<T extends FieldValues> {\n readonly control: Control<T>;\n readonly setValue: UseFormSetValue<T>;\n readonly countryIso: string;\n readonly addressLineField: Path<T>;\n readonly cityField: Path<T>;\n readonly stateField: Path<T>;\n readonly postalCodeField: Path<T>;\n readonly stateOptions?: readonly StateOption[];\n readonly id?: string;\n readonly label?: string;\n readonly placeholder?: string;\n readonly disabled?: boolean;\n}\n\nconst SET_OPTS = {\n shouldValidate: true,\n shouldDirty: true,\n shouldTouch: true,\n} as const;\n\nconst DEBOUNCE_MS = 250;\nconst EMPTY_STATE_OPTIONS: readonly StateOption[] = [];\n\nexport function AddressAutocompleteInput<T extends FieldValues>({\n control,\n setValue,\n countryIso,\n addressLineField,\n cityField,\n stateField,\n postalCodeField,\n stateOptions = EMPTY_STATE_OPTIONS,\n id,\n label,\n placeholder,\n disabled,\n}: AddressAutocompleteInputProps<T>): JSX.Element {\n const api = useAddressAutocompleteApi();\n const {\n field,\n fieldState: { error },\n } = useController({ name: addressLineField, control });\n const [suggestions, setSuggestions] = useState<readonly AddressSuggestion[]>(\n [],\n );\n const [isOpen, setIsOpen] = useState(false);\n const [needsCountry, setNeedsCountry] = useState(false);\n const containerRef = useRef<HTMLDivElement | null>(null);\n // Monotonically increasing request ID. We only apply a fetch result when its\n // ID still matches the latest issued one — guards against stale responses\n // arriving out of order under network jitter.\n const requestIdRef = useRef(0);\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isCountrySelected = useMemo(\n () => Boolean(countryIso && countryIso.trim() !== \"\"),\n [countryIso],\n );\n const inputId = id ?? addressLineField;\n\n useEffect(() => {\n function onClickOutside(e: MouseEvent) {\n if (\n containerRef.current &&\n !containerRef.current.contains(e.target as Node)\n ) {\n setIsOpen(false);\n setNeedsCountry(false);\n }\n }\n document.addEventListener(\"mousedown\", onClickOutside);\n return () => document.removeEventListener(\"mousedown\", onClickOutside);\n }, []);\n\n useEffect(\n () => () => {\n if (debounceTimerRef.current !== null) {\n clearTimeout(debounceTimerRef.current);\n }\n },\n [],\n );\n\n const populate = useCallback(\n (s: AddressDetails) => {\n setValue(\n addressLineField,\n s.streetLine as PathValue<T, Path<T>>,\n SET_OPTS,\n );\n setValue(cityField, s.city as PathValue<T, Path<T>>, SET_OPTS);\n setValue(\n stateField,\n resolveAdministrativeAreaState(s, stateOptions) as PathValue<\n T,\n Path<T>\n >,\n SET_OPTS,\n );\n setValue(\n postalCodeField,\n s.postalCode as PathValue<T, Path<T>>,\n SET_OPTS,\n );\n },\n [\n addressLineField,\n cityField,\n postalCodeField,\n setValue,\n stateField,\n stateOptions,\n ],\n );\n\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n field.onChange(value);\n\n // Cancel any pending fetch from a prior keystroke.\n if (debounceTimerRef.current !== null) {\n clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = null;\n }\n\n if (value.length < 1) {\n setIsOpen(false);\n setNeedsCountry(false);\n setSuggestions([]);\n return;\n }\n if (!isCountrySelected) {\n setNeedsCountry(true);\n setIsOpen(true);\n return;\n }\n setNeedsCountry(false);\n\n const requestId = ++requestIdRef.current;\n debounceTimerRef.current = setTimeout(() => {\n debounceTimerRef.current = null;\n void api.fetchSuggestions({ query: value, countryIso }).then((next) => {\n // Drop stale responses — a newer keystroke has already issued a\n // request, and applying this would clobber its result.\n if (requestId !== requestIdRef.current) return;\n setSuggestions(next);\n setIsOpen(true);\n });\n }, DEBOUNCE_MS);\n },\n [api, countryIso, field, isCountrySelected],\n );\n\n const handleClick = useCallback(\n async (suggestion: AddressSuggestion) => {\n // Cancel any debounced fetch still pending from a recent keystroke and\n // advance the request ID so any in-flight response is dropped — without\n // this, the timer could fire after a selection and re-open the dropdown.\n if (debounceTimerRef.current !== null) {\n clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = null;\n }\n const clickId = ++requestIdRef.current;\n\n if (!suggestion.needsDetailLookup) {\n populate({\n streetLine: suggestion.streetLine ?? suggestion.text,\n city: suggestion.city ?? \"\",\n state: suggestion.state ?? \"\",\n postalCode: suggestion.postalCode ?? \"\",\n });\n setIsOpen(false);\n return;\n }\n const { details, drilldownSuggestions } = await api.fetchDetails({\n suggestion,\n countryIso,\n });\n // Drop the result if the user typed during the network round-trip — a\n // newer keystroke has superseded this click and applying its result\n // would clobber what the user has since entered.\n if (clickId !== requestIdRef.current) return;\n if (details) {\n populate(details);\n setIsOpen(false);\n return;\n }\n setSuggestions(drilldownSuggestions);\n setIsOpen(true);\n },\n [api, countryIso, populate],\n );\n\n return (\n <div className=\"space-y-1\">\n {label && (\n <Label htmlFor={inputId} className=\"mb-1.5 block text-sm font-medium\">\n {label}\n </Label>\n )}\n <div ref={containerRef} className=\"relative\">\n <Input\n id={inputId}\n type=\"text\"\n ref={field.ref}\n value={(field.value as string) ?? \"\"}\n onChange={handleChange}\n onBlur={field.onBlur}\n placeholder={placeholder}\n disabled={disabled}\n aria-invalid={!!error}\n />\n {isOpen && needsCountry && (\n <div className=\"bg-muted text-foreground absolute z-50 mt-1 w-full rounded-md px-3 py-2 text-sm shadow-lg\">\n Please select a country first to enable address suggestions\n </div>\n )}\n {isOpen && !needsCountry && suggestions.length > 0 && (\n <div className=\"bg-background text-foreground border-border absolute z-50 mt-1 max-h-60 w-full overflow-y-auto rounded-md border shadow-lg\">\n {suggestions.map((s) => (\n <button\n key={s.id}\n type=\"button\"\n onClick={() => {\n void handleClick(s).catch((err) => {\n // handleClick is async because of fetchDetails — the current\n // Smarty adapter and NULL_API swallow errors, so this is\n // belt-and-suspenders for future adapters that might throw.\n console.error(\"Address autocomplete click failed:\", err);\n });\n }}\n className=\"hover:bg-muted block w-full cursor-pointer px-3 py-2 text-left text-sm\"\n >\n {s.text}\n {s.needsDetailLookup ? \" →\" : \"\"}\n </button>\n ))}\n </div>\n )}\n </div>\n {error?.message && (\n <p className=\"text-destructive text-sm\">{error.message}</p>\n )}\n </div>\n );\n}\n","// Helper to render the device fingerprinting iframe.\n//\n// Browser-only: the function body requires `document` — never call during\n// SSR. Importing this module is SSR-safe; only invocation touches the DOM.\nexport const renderFingerprintingIframe = (html: string): Promise<void> => {\n return new Promise((resolve) => {\n const iframe = document.createElement(\"iframe\");\n iframe.srcdoc = html;\n iframe.style.display = \"none\";\n\n document.body.appendChild(iframe);\n\n // Wait for fingerprinting to complete (usually <10 seconds)\n setTimeout(() => {\n document.body.removeChild(iframe);\n resolve();\n }, 10000);\n });\n};\n","import type { ThreeDsRealtimeTransport } from \"@fluid-app/fluid-pay-ui\";\nimport type { ThreeDSStatus } from \"@fluid-app/fluid-pay-core/types\";\nimport type { ThreeDsSessionState } from \"@fluid-app/portal-core/pay-types\";\n\n/**\n * Polling-only realtime transport. A no-op channel keeps useVgs3DSCompletion's\n * `hasPusher` true so it does not log the misleading \"subscribe returned null\"\n * error; the 3s status poll is the sole resolver. The Pusher fast-follow\n * replaces this constant with a real pusher-js client.\n */\nexport const POLLING_ONLY_TRANSPORT: ThreeDsRealtimeTransport = {\n subscribe: () => ({ bind: () => {}, unbind: () => {} }),\n unsubscribe: () => {},\n};\n\n/**\n * Map the BFF 3ds_status session state -> the FE-A hook's ThreeDSStatus. Only\n * the known terminal states resolve the poll (approved/skipped -> completed,\n * denied/timed_out/expired -> failed, cancelled -> canceled); any other value\n * (initialized/pending or a future backend state) stays non-terminal so the\n * poll keeps running until the EMVCo timeout, rather than being mis-classified.\n */\nexport function mapSessionStateToThreeDSStatus(\n state: ThreeDsSessionState,\n expired: boolean | null,\n): ThreeDSStatus {\n return {\n completed: state === \"approved\" || state === \"skipped\",\n failed: state === \"denied\" || state === \"timed_out\" || expired === true,\n canceled: state === \"cancelled\",\n status: state,\n };\n}\n\n/**\n * Dig the portal BFF error code off a thrown ApiError. Structural and\n * self-validating: a non-ApiError simply yields undefined (no instanceof gate).\n */\nexport function cardAdd3DSErrorCode(error: unknown): string | undefined {\n const data = (error as { data?: unknown } | null)?.data;\n const meta = (data as { meta?: unknown } | null)?.meta;\n const code = (meta as { error_code?: unknown } | null)?.error_code;\n return typeof code === \"string\" ? code : undefined;\n}\n\n/**\n * Customer-facing message off a thrown portal ApiError. The client lifts the\n * portal `{ error: { message } }` onto ApiError.message; fall back to the\n * nested field, then the supplied generic. (`data.error_message` is the\n * storefront shape and is always undefined here.)\n */\nexport function cardAdd3DSErrorMessage(\n error: unknown,\n fallback: string,\n): string {\n if (error instanceof Error && error.message) return error.message;\n const data = (error as { data?: unknown } | null)?.data;\n const nested = (data as { error?: { message?: unknown } } | null)?.error\n ?.message;\n return typeof nested === \"string\" ? nested : fallback;\n}\n","import { useEffectEvent, useRef, useState, type ReactNode } from \"react\";\nimport { renderFingerprintingIframe } from \"@fluid-app/fluid-pay-core/utils/fingerprinting\";\nimport type { ThreeDSStatus } from \"@fluid-app/fluid-pay-core/types\";\nimport { useVgs3DSCompletion, VgsThreeDSModal } from \"@fluid-app/fluid-pay-ui\";\nimport type { PayApi } from \"@fluid-app/portal-core/pay-api\";\nimport type {\n ConfirmApprovedResult,\n ThreeDsRequiredCreateResult,\n} from \"@fluid-app/portal-core/pay-types\";\nimport {\n POLLING_ONLY_TRANSPORT,\n cardAdd3DSErrorCode,\n cardAdd3DSErrorMessage,\n mapSessionStateToThreeDSStatus,\n} from \"./three-ds-card-add-helpers\";\n\n// Wait before the single confirm retry when a completion signal races the\n// backend's upstream reconcile (422 three_ds_pending).\nconst PENDING_RETRY_DELAY_MS = 2_000;\nconst waitFor = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\ninterface ActiveSession {\n sessionToken: string;\n merchantTxId: string | undefined;\n}\ninterface PendingChallenge {\n html: string;\n url?: string;\n}\n\nexport interface UsePortalCardAdd3DSFlowOptions {\n payApi: PayApi;\n onApproved: (approved: ConfirmApprovedResult) => void | Promise<void>;\n onFailed: (message: string) => void;\n onCanceled: () => void;\n t: (key: string) => string;\n}\n\nexport interface PortalCardAdd3DSFlow {\n begin: (created: ThreeDsRequiredCreateResult) => Promise<void>;\n modal: ReactNode;\n isActive: boolean;\n}\n\nexport function usePortalCardAdd3DSFlow({\n payApi,\n onApproved,\n onFailed,\n onCanceled,\n t,\n}: UsePortalCardAdd3DSFlowOptions): PortalCardAdd3DSFlow {\n const [session, setSession] = useState<ActiveSession | null>(null);\n const [challenge, setChallenge] = useState<PendingChallenge | null>(null);\n // Synchronous mirror for the single-flight guard and async continuations.\n const activeSessionRef = useRef<ActiveSession | null>(null);\n\n // Always call the latest surface callbacks.\n const emitApproved = useEffectEvent((approved: ConfirmApprovedResult) =>\n onApproved(approved),\n );\n const emitFailed = useEffectEvent((message: string) => onFailed(message));\n const emitCanceled = useEffectEvent(() => onCanceled());\n\n const resetFlow = () => {\n activeSessionRef.current = null;\n setSession(null);\n setChallenge(null);\n };\n\n // Exactly-one-terminal guard: each finish* no-ops once a terminal has won\n // (resetFlow nulls the ref right before the emit). Covers the modal's\n // Cancel/timeout and begin()'s catch racing a terminal.\n const finishApproved = async (approved: ConfirmApprovedResult) => {\n if (!activeSessionRef.current) return;\n resetFlow();\n await emitApproved(approved);\n };\n const finishFailed = (message: string) => {\n if (!activeSessionRef.current) return;\n resetFlow();\n emitFailed(message);\n };\n const finishCanceled = () => {\n if (!activeSessionRef.current) return;\n resetFlow();\n emitCanceled();\n };\n\n const failureMessageFrom = (error: unknown): string => {\n if (cardAdd3DSErrorCode(error) === \"three_ds_session_not_found\") {\n return t(\"payment_verification_timed_out\");\n }\n return cardAdd3DSErrorMessage(\n error,\n t(\"payment_verification_didnt_complete\"),\n );\n };\n\n const begin = async (created: ThreeDsRequiredCreateResult): Promise<void> => {\n // Single-flight: one bank check at a time.\n if (activeSessionRef.current) return;\n\n const {\n session_token,\n merchant_tx_id,\n fingerprinting_required,\n tds_method_content,\n } = created.three_ds;\n // No attempt activated yet — emit directly (the finish* ref-guard would swallow this).\n if (!session_token) {\n emitFailed(t(\"payment_verification_didnt_complete\"));\n return;\n }\n\n const active: ActiveSession = {\n sessionToken: session_token,\n merchantTxId: merchant_tx_id ?? undefined,\n };\n activeSessionRef.current = active;\n setSession(active);\n\n try {\n // Inside the try so a synchronous throw lands on finishFailed — an\n // escape would leave the ref set and deadlock single-flight.\n if (\n fingerprinting_required !== false &&\n typeof tds_method_content === \"string\" &&\n tds_method_content.length > 0\n ) {\n await renderFingerprintingIframe(tds_method_content);\n }\n\n const confirmation = await payApi.confirmPaymentMethod3DS({\n session_token: active.sessionToken,\n });\n if (confirmation.status === \"approved\") {\n await finishApproved(confirmation);\n return;\n }\n // A challenge with no form renders an empty modal that strands the user\n // — fail fast. (challenge_url alone is only the POST target for the\n // form's hidden fields; the modal renders the iframe from the form HTML.)\n if (!confirmation.challenge_form) {\n finishFailed(t(\"payment_verification_didnt_complete\"));\n return;\n }\n setChallenge({\n html: confirmation.challenge_form,\n url: confirmation.challenge_url ?? undefined,\n });\n } catch (error) {\n finishFailed(failureMessageFrom(error));\n }\n };\n\n // Replays confirm after a completion signal. The modal stays open (its\n // Cancel/timeout terminal can fire mid-await), so check staleness against\n // the attempt captured at entry after every await — land on EXACTLY ONE\n // terminal.\n const reconfirmAfterSignal = async (\n allowPendingRetry: boolean,\n ): Promise<void> => {\n const active = activeSessionRef.current;\n if (!active) return;\n try {\n const confirmation = await payApi.confirmPaymentMethod3DS({\n session_token: active.sessionToken,\n });\n if (activeSessionRef.current !== active) return;\n if (confirmation.status === \"approved\") {\n await finishApproved(confirmation);\n return;\n }\n // A second challenge after a completion signal is out of contract.\n finishFailed(t(\"payment_verification_didnt_complete\"));\n } catch (error) {\n if (activeSessionRef.current !== active) return;\n if (\n allowPendingRetry &&\n cardAdd3DSErrorCode(error) === \"three_ds_pending\"\n ) {\n await waitFor(PENDING_RETRY_DELAY_MS);\n if (activeSessionRef.current !== active) return;\n await reconfirmAfterSignal(false);\n return;\n }\n finishFailed(failureMessageFrom(error));\n }\n };\n\n const sessionStatusSnapshot = async (): Promise<ThreeDSStatus | null> => {\n const active = activeSessionRef.current;\n if (!active) return null;\n try {\n const snapshot = await payApi.fetchPaymentMethod3DSStatus({\n session_token: active.sessionToken,\n });\n return mapSessionStateToThreeDSStatus(snapshot.state, snapshot.expired);\n } catch {\n return null; // transient — keep polling\n }\n };\n\n useVgs3DSCompletion({\n channelName: session ? `private-3ds-session-${session.sessionToken}` : \"\",\n transport: POLLING_ONLY_TRANSPORT,\n checkStatus: sessionStatusSnapshot,\n merchantTxId: session?.merchantTxId,\n enabled: challenge !== null,\n onComplete: () => {\n void reconfirmAfterSignal(true);\n },\n onError: (message) => finishFailed(message),\n onCanceled: finishCanceled,\n });\n\n const closeChallengeModal = (timeoutError?: string) => {\n const active = activeSessionRef.current;\n // Fire-and-forget server-side cancel; the close never waits on it.\n if (active) {\n void payApi\n .cancelPaymentMethod3DS({ session_token: active.sessionToken })\n .catch(() => {});\n }\n if (timeoutError) finishFailed(timeoutError);\n else finishCanceled();\n };\n\n const modal = (\n <VgsThreeDSModal\n isOpen={challenge !== null}\n challengeHtml={challenge?.html ?? \"\"}\n challengeUrl={challenge?.url}\n onClose={closeChallengeModal}\n labels={{\n title: t(\"verify_your_payment\"),\n cancel: t(\"cancel\"),\n timedOut: t(\"payment_verification_timed_out\"),\n stillWorking: t(\"still_working_with_your_bank\"),\n dontRefresh: t(\"dont_refresh_this_tab\"),\n }}\n />\n );\n\n return { begin, modal, isActive: session !== null };\n}\n"],"mappings":"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"}
|