diff --git a/packages/mint-components/package.json b/packages/mint-components/package.json index 4bdfc1be86..17b002aab2 100644 --- a/packages/mint-components/package.json +++ b/packages/mint-components/package.json @@ -1,7 +1,7 @@ { "name": "@saasquatch/mint-components", "title": "Mint Components", - "version": "2.1.7", + "version": "2.1.9-21", "description": "A minimal design library with components for referral and loyalty experiences. Built with Shoelace components by Saasquatch.", "icon": "https://res.cloudinary.com/saasquatch/image/upload/v1652219900/squatch-assets/For_Mint.svg", "raisins": "docs/raisins.json", diff --git a/packages/mint-components/src/components.d.ts b/packages/mint-components/src/components.d.ts index 0916004dd4..c773798d53 100644 --- a/packages/mint-components/src/components.d.ts +++ b/packages/mint-components/src/components.d.ts @@ -34,6 +34,7 @@ import { NavigationMenuViewProps } from "./components/sqm-navigation-menu/sqm-na import { NavigationSidebarViewProps } from "./components/sqm-navigation-sidebar/sqm-navigation-sidebar-view"; import { NavigationSidebarItemViewProps } from "./components/sqm-navigation-sidebar-item/sqm-navigation-sidebar-item-view"; import { UsePagination } from "./components/sqm-pagination/usePagination"; +import { PartnerInfoModalResult } from "./components/sqm-partner-info-modal/usePartnerInfoModal"; import { PasswordFieldViewDemoProps } from "./components/sqm-password-field/usePasswordField"; import { PayoutButtonScrollViewProps } from "./components/sqm-payout-button-scroll/sqm-payout-button-scroll-view"; import { PayoutStatusAlertViewProps } from "./components/tax-and-cash/sqm-payout-status-alert/sqm-payout-status-alert-view"; @@ -64,6 +65,12 @@ export namespace Components { "renderLabel": (idx: any) => Promise; } interface SqmBankingInfoForm { + /** + * Error messages for the agency code field. Supports error codes: empty, alphanumeric, tooShort + * @uiName Agency code error + * @uiWidget textArea + */ + "agencyCodeError": string; /** * @uiName Agency code field label */ @@ -73,18 +80,42 @@ export namespace Components { * @uiName Back button label */ "backButton": string; + /** + * Error messages for the bank account number / IBAN field. Supports error codes: empty, invalidUk, invalid, ibanEmpty, ibanAlphanumeric, ibanInvalid, ibanCountryMismatch + * @uiName Bank account number / IBAN error + * @uiWidget textArea + */ + "bankAccountNumberError": string; /** * @uiName Bank account number field label */ "bankAccountNumberLabel": string; + /** + * Error messages for the bank account type field. Supports error codes: empty + * @uiName Bank account type error + * @uiWidget textArea + */ + "bankAccountTypeError": string; /** * @uiName Bank account type field label */ "bankAccountTypeLabel": string; + /** + * Error messages for the bank address field. Supports error codes: empty + * @uiName Bank address error + * @uiWidget textArea + */ + "bankAddressError": string; /** * @uiName Bank address field label */ "bankAddressLabel": string; + /** + * Error messages for the bank city field. Supports error codes: empty + * @uiName Bank city error + * @uiWidget textArea + */ + "bankCityError": string; /** * @uiName Bank city field label */ @@ -93,14 +124,32 @@ export namespace Components { * @uiName Bank country field label */ "bankLocationLabel": string; + /** + * Error messages for the bank name field. Supports error codes: empty + * @uiName Bank name error + * @uiWidget textArea + */ + "bankNameError": string; /** * @uiName Bank name field label */ "bankNameLabel": string; + /** + * Error messages for the bank postal code field. Supports error codes: empty + * @uiName Bank postal code error + * @uiWidget textArea + */ + "bankPostalCodeError": string; /** * @uiName Bank postal code field label */ "bankPostalCodeLabel": string; + /** + * Error messages for the bank province/state field. Supports error codes: empty + * @uiName Bank province/state error + * @uiWidget textArea + */ + "bankStateError": string; /** * @uiName Bank province/state field label */ @@ -109,14 +158,32 @@ export namespace Components { * @uiName Beneficiary account field description */ "beneficiaryAccountNameDescription": string; + /** + * Error messages for the beneficiary / account holder name field. Supports error codes: empty, invalidCharacters, numeric, tooLong, nonEnglish, businessNameMismatch, nameMismatch, businessPayeeMismatch, payeeMismatch + * @uiName Beneficiary account name error + * @uiWidget textArea + */ + "beneficiaryAccountNameError": string; /** * @uiName Beneficiary account field label */ "beneficiaryAccountNameLabel": string; + /** + * Error messages for the branch code field. Supports error codes: invalid + * @uiName Branch code error + * @uiWidget textArea + */ + "branchCodeError": string; /** * @uiName Branch code field label */ "branchCodeLabel": string; + /** + * Error messages for the branch name field. Supports error codes: empty + * @uiName Branch name error + * @uiWidget textArea + */ + "branchNameError": string; /** * One of three options listed for the classification field * @uiName Business classification option @@ -130,6 +197,12 @@ export namespace Components { * @uiName Classification CPF field label */ "classificationCPFLabel": string; + /** + * Error messages for the classification code field. Supports error codes: empty, invalidKzt + * @uiName Classification code error + * @uiWidget textArea + */ + "classificationCodeError": string; /** * @uiName Classification entity field label */ @@ -237,6 +310,12 @@ export namespace Components { * @uiName Information modal title */ "modalTitle": string; + /** + * Error messages for the patronymic name field. Supports error codes: empty, alphanumeric + * @uiName Patronymic name error + * @uiWidget textArea + */ + "patronymicNameError": string; /** * @uiName Patronymic name field label */ @@ -246,6 +325,12 @@ export namespace Components { * @uiName PayPal email field label */ "payPalInputLabel": string; + /** + * Error messages for the payment day field. Supports error codes: empty, invalid + * @uiName Payment day error + * @uiWidget textArea + */ + "paymentDayError": string; /** * Label text for the payment day select option for the fifteenth of the month * @uiName Fifteenth of month payday option @@ -281,11 +366,29 @@ export namespace Components { * @uiName Fixed day payment schedule option */ "paymentScheduleFixedDay": string; + /** + * Error messages for the payment threshold field. Supports error codes: empty, invalid + * @uiName Payment threshold error + * @uiWidget textArea + */ + "paymentThresholdError": string; /** * Participant use this field to select the balance at which they want to be paid * @uiName Payment threshold field label */ "paymentThresholdSelectLabel": string; + /** + * Error messages for the PayPal email field. Supports error codes: empty, unsupportedCurrency, invalidEmail, verificationIncomplete + * @uiName PayPal email error + * @uiWidget textArea + */ + "paypalEmailError": string; + /** + * Error messages for the routing code / sort code / BSB field. Supports error codes: invalidBsb, invalidSortCode, empty, invalid + * @uiName Routing code error + * @uiWidget textArea + */ + "routingCodeError": string; /** * @uiName Routing code field label */ @@ -303,6 +406,12 @@ export namespace Components { * @uiName Support link text */ "supportLink": string; + /** + * Error messages for the SWIFT / BIC code field. Supports error codes: empty, alphanumeric, invalid + * @uiName SWIFT code error + * @uiWidget textArea + */ + "swiftCodeError": string; /** * @uiName SWIFT code field label */ @@ -316,6 +425,12 @@ export namespace Components { * @uiName Page description */ "taxAndPayoutsDescription": string; + /** + * Error messages for the tax payer ID / classification entity field. Supports error codes: empty, emptyAr, emptyKr, alphanumeric, alphanumericAr, alphanumericKr, invalid, invalidAr, invalidKr, invalidKzt, cnpjTooShort, cpfTooShort + * @uiName Tax payer ID error + * @uiWidget textArea + */ + "taxPayerIdError": string; /** * @uiName Taxpayer ID field label */ @@ -333,6 +448,12 @@ export namespace Components { * @uiName Verify email header */ "verifyEmailHeaderText": string; + /** + * Error messages for the VO code field. Supports error codes: empty, alphanumeric + * @uiName VO code error + * @uiWidget textArea + */ + "voCodeError": string; /** * @uiName VO code field label */ @@ -2190,6 +2311,110 @@ export namespace Components { */ "paginationText": string; } + interface SqmPartnerInfoModal { + /** + * Edit the property called terms and conditions text to change what's displayed for {termsAndConditionsLink}. + * @uiName Terms and conditions checkbox + */ + "allowBankingCollection": string; + /** + * Brand name shown in the modal header + * @uiName Brand name + */ + "brandName": string; + /** + * @uiName Confirm button label + */ + "confirmButtonLabel": string; + /** + * @uiName Country label + */ + "countryLabel": string; + /** + * @uiName Currency label + */ + "currencyLabel": string; + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + /** + * Description for existing partner confirmation + * @uiName Existing partner description + * @uiWidget textArea + */ + "descriptionExistingPartner": string; + /** + * Description for new partner setup + * @uiName New partner description + * @uiWidget textArea + */ + "descriptionNewPartner": string; + /** + * Used to render in another modal. + * @undocumented + */ + "inModal": boolean; + /** + * @uiName Missing fields error text + * @uiWidget textArea + */ + "missingFieldsErrorText": string; + /** + * Header text when user has no existing partner + * @uiName New partner header + * @uiWidget textArea + */ + "modalHeader": string; + /** + * Header text when user has an existing partner + * @uiName Existing partner header + * @uiWidget textArea + */ + "modalHeaderExistingPartner": string; + /** + * @uiName Network error text + * @uiWidget textArea + */ + "networkErrorText": string; + /** + * @uiName Search country placeholder + */ + "searchCountryPlaceholder": string; + /** + * @uiName Search currency placeholder + */ + "searchCurrencyPlaceholder": string; + /** + * @undocumented + * @componentState { "title": "New partner", "props": { "states": { "open": true, "isExistingPartner": false } } } + * @componentState { "title": "Existing partner", "props": { "states": { "open": true, "isExistingPartner": true, "countryCode": "US", "currency": "USD" } } } + * @componentState { "title": "Connected (hidden)", "props": { "states": { "open": false } } } + */ + "stateController": string; + /** + * @uiName Submit button label + */ + "submitButtonLabel": string; + /** + * Support description for existing partner confirmation + * @uiName Existing partner support description + * @uiWidget textArea + */ + "supportDescriptionExistingPartner": string; + /** + * The link text that appears in the terms and conditions checkbox + * @uiName Terms and conditions text + * @uiWidget textArea + */ + "termsAndConditionsLabel": string; + /** + * The link that appears in the terms and conditions checkbox + * @uiName Terms and conditions link + */ + "termsAndConditionsLink": string; + } interface SqmPasswordField { /** * @undocumented @@ -7026,6 +7251,78 @@ export namespace Components { * @uiGroup Code Verification Step */ "codeStep_verifyText": string; + /** + * @uiName Confirm button label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_confirmButtonLabel": string; + /** + * @uiName Country label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_countryLabel": string; + /** + * @uiName Currency label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_currencyLabel": string; + /** + * @uiName Existing partner description + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_descriptionExistingPartner": string; + /** + * @uiName New partner description + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_descriptionNewPartner": string; + /** + * @uiName Missing fields error text + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_missingFieldsErrorText": string; + /** + * @uiName New partner header + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_modalHeader": string; + /** + * @uiName Existing partner header + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_modalHeaderExistingPartner": string; + /** + * @uiName Network error text + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_networkErrorText": string; + /** + * @uiName Search country placeholder + * @uiGroup Partner Creation Step + */ + "createPartnerStep_searchCountryPlaceholder": string; + /** + * @uiName Search currency placeholder + * @uiGroup Partner Creation Step + */ + "createPartnerStep_searchCurrencyPlaceholder": string; + /** + * @uiName Submit button label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_submitButtonLabel": string; + /** + * @uiName Existing partner support description + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_supportDescriptionExistingPartner": string; /** * @uiName Email input label * @uiGroup Email Verification Step @@ -7066,10 +7363,16 @@ export namespace Components { * @uiGroup General Text */ "general_verifyEmailHeader": string; + /** + * @uiName General widget header text with partner creation + * @uiGroup General Text + */ + "general_widgetHeaderWithPartnerCreation": string; /** * @undocumented * @componentState { "title": "Step 1: Enter email", "props": { "showCode": false }, "dependencies": ["sqm-email-verification"], "uiGroup": "Email Verification Step" } * @componentState { "title": "Step 2: Enter code", "props": { "showCode": true }, "dependencies": ["sqm-code-verification"], "uiGroup": "Code Verification Step" } + * @componentState { "title": "Step 3: Create Partner", "props": { "showPartnerModal": true }, "dependencies": ["sqm-partner-info-modal"], "uiGroup": "Partner Creation Step" } */ "stateController": string; } @@ -7377,6 +7680,12 @@ declare global { prototype: HTMLSqmPaginationElement; new (): HTMLSqmPaginationElement; }; + interface HTMLSqmPartnerInfoModalElement extends Components.SqmPartnerInfoModal, HTMLStencilElement { + } + var HTMLSqmPartnerInfoModalElement: { + prototype: HTMLSqmPartnerInfoModalElement; + new (): HTMLSqmPartnerInfoModalElement; + }; interface HTMLSqmPasswordFieldElement extends Components.SqmPasswordField, HTMLStencilElement { } var HTMLSqmPasswordFieldElement: { @@ -7901,6 +8210,7 @@ declare global { "sqm-navigation-sidebar": HTMLSqmNavigationSidebarElement; "sqm-navigation-sidebar-item": HTMLSqmNavigationSidebarItemElement; "sqm-pagination": HTMLSqmPaginationElement; + "sqm-partner-info-modal": HTMLSqmPartnerInfoModalElement; "sqm-password-field": HTMLSqmPasswordFieldElement; "sqm-payout-button-scroll": HTMLSqmPayoutButtonScrollElement; "sqm-payout-details-card": HTMLSqmPayoutDetailsCardElement; @@ -7986,6 +8296,12 @@ declare namespace LocalJSX { interface RaisinsPlopTarget { } interface SqmBankingInfoForm { + /** + * Error messages for the agency code field. Supports error codes: empty, alphanumeric, tooShort + * @uiName Agency code error + * @uiWidget textArea + */ + "agencyCodeError"?: string; /** * @uiName Agency code field label */ @@ -7995,18 +8311,42 @@ declare namespace LocalJSX { * @uiName Back button label */ "backButton"?: string; + /** + * Error messages for the bank account number / IBAN field. Supports error codes: empty, invalidUk, invalid, ibanEmpty, ibanAlphanumeric, ibanInvalid, ibanCountryMismatch + * @uiName Bank account number / IBAN error + * @uiWidget textArea + */ + "bankAccountNumberError"?: string; /** * @uiName Bank account number field label */ "bankAccountNumberLabel"?: string; + /** + * Error messages for the bank account type field. Supports error codes: empty + * @uiName Bank account type error + * @uiWidget textArea + */ + "bankAccountTypeError"?: string; /** * @uiName Bank account type field label */ "bankAccountTypeLabel"?: string; + /** + * Error messages for the bank address field. Supports error codes: empty + * @uiName Bank address error + * @uiWidget textArea + */ + "bankAddressError"?: string; /** * @uiName Bank address field label */ "bankAddressLabel"?: string; + /** + * Error messages for the bank city field. Supports error codes: empty + * @uiName Bank city error + * @uiWidget textArea + */ + "bankCityError"?: string; /** * @uiName Bank city field label */ @@ -8015,14 +8355,32 @@ declare namespace LocalJSX { * @uiName Bank country field label */ "bankLocationLabel"?: string; + /** + * Error messages for the bank name field. Supports error codes: empty + * @uiName Bank name error + * @uiWidget textArea + */ + "bankNameError"?: string; /** * @uiName Bank name field label */ "bankNameLabel"?: string; + /** + * Error messages for the bank postal code field. Supports error codes: empty + * @uiName Bank postal code error + * @uiWidget textArea + */ + "bankPostalCodeError"?: string; /** * @uiName Bank postal code field label */ "bankPostalCodeLabel"?: string; + /** + * Error messages for the bank province/state field. Supports error codes: empty + * @uiName Bank province/state error + * @uiWidget textArea + */ + "bankStateError"?: string; /** * @uiName Bank province/state field label */ @@ -8031,14 +8389,32 @@ declare namespace LocalJSX { * @uiName Beneficiary account field description */ "beneficiaryAccountNameDescription"?: string; + /** + * Error messages for the beneficiary / account holder name field. Supports error codes: empty, invalidCharacters, numeric, tooLong, nonEnglish, businessNameMismatch, nameMismatch, businessPayeeMismatch, payeeMismatch + * @uiName Beneficiary account name error + * @uiWidget textArea + */ + "beneficiaryAccountNameError"?: string; /** * @uiName Beneficiary account field label */ "beneficiaryAccountNameLabel"?: string; + /** + * Error messages for the branch code field. Supports error codes: invalid + * @uiName Branch code error + * @uiWidget textArea + */ + "branchCodeError"?: string; /** * @uiName Branch code field label */ "branchCodeLabel"?: string; + /** + * Error messages for the branch name field. Supports error codes: empty + * @uiName Branch name error + * @uiWidget textArea + */ + "branchNameError"?: string; /** * One of three options listed for the classification field * @uiName Business classification option @@ -8052,6 +8428,12 @@ declare namespace LocalJSX { * @uiName Classification CPF field label */ "classificationCPFLabel"?: string; + /** + * Error messages for the classification code field. Supports error codes: empty, invalidKzt + * @uiName Classification code error + * @uiWidget textArea + */ + "classificationCodeError"?: string; /** * @uiName Classification entity field label */ @@ -8159,6 +8541,12 @@ declare namespace LocalJSX { * @uiName Information modal title */ "modalTitle"?: string; + /** + * Error messages for the patronymic name field. Supports error codes: empty, alphanumeric + * @uiName Patronymic name error + * @uiWidget textArea + */ + "patronymicNameError"?: string; /** * @uiName Patronymic name field label */ @@ -8168,6 +8556,12 @@ declare namespace LocalJSX { * @uiName PayPal email field label */ "payPalInputLabel"?: string; + /** + * Error messages for the payment day field. Supports error codes: empty, invalid + * @uiName Payment day error + * @uiWidget textArea + */ + "paymentDayError"?: string; /** * Label text for the payment day select option for the fifteenth of the month * @uiName Fifteenth of month payday option @@ -8203,11 +8597,29 @@ declare namespace LocalJSX { * @uiName Fixed day payment schedule option */ "paymentScheduleFixedDay"?: string; + /** + * Error messages for the payment threshold field. Supports error codes: empty, invalid + * @uiName Payment threshold error + * @uiWidget textArea + */ + "paymentThresholdError"?: string; /** * Participant use this field to select the balance at which they want to be paid * @uiName Payment threshold field label */ "paymentThresholdSelectLabel"?: string; + /** + * Error messages for the PayPal email field. Supports error codes: empty, unsupportedCurrency, invalidEmail, verificationIncomplete + * @uiName PayPal email error + * @uiWidget textArea + */ + "paypalEmailError"?: string; + /** + * Error messages for the routing code / sort code / BSB field. Supports error codes: invalidBsb, invalidSortCode, empty, invalid + * @uiName Routing code error + * @uiWidget textArea + */ + "routingCodeError"?: string; /** * @uiName Routing code field label */ @@ -8225,6 +8637,12 @@ declare namespace LocalJSX { * @uiName Support link text */ "supportLink"?: string; + /** + * Error messages for the SWIFT / BIC code field. Supports error codes: empty, alphanumeric, invalid + * @uiName SWIFT code error + * @uiWidget textArea + */ + "swiftCodeError"?: string; /** * @uiName SWIFT code field label */ @@ -8238,6 +8656,12 @@ declare namespace LocalJSX { * @uiName Page description */ "taxAndPayoutsDescription"?: string; + /** + * Error messages for the tax payer ID / classification entity field. Supports error codes: empty, emptyAr, emptyKr, alphanumeric, alphanumericAr, alphanumericKr, invalid, invalidAr, invalidKr, invalidKzt, cnpjTooShort, cpfTooShort + * @uiName Tax payer ID error + * @uiWidget textArea + */ + "taxPayerIdError"?: string; /** * @uiName Taxpayer ID field label */ @@ -8255,6 +8679,12 @@ declare namespace LocalJSX { * @uiName Verify email header */ "verifyEmailHeaderText"?: string; + /** + * Error messages for the VO code field. Supports error codes: empty, alphanumeric + * @uiName VO code error + * @uiWidget textArea + */ + "voCodeError"?: string; /** * @uiName VO code field label */ @@ -10107,6 +10537,110 @@ declare namespace LocalJSX { */ "paginationText"?: string; } + interface SqmPartnerInfoModal { + /** + * Edit the property called terms and conditions text to change what's displayed for {termsAndConditionsLink}. + * @uiName Terms and conditions checkbox + */ + "allowBankingCollection"?: string; + /** + * Brand name shown in the modal header + * @uiName Brand name + */ + "brandName"?: string; + /** + * @uiName Confirm button label + */ + "confirmButtonLabel"?: string; + /** + * @uiName Country label + */ + "countryLabel"?: string; + /** + * @uiName Currency label + */ + "currencyLabel"?: string; + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + /** + * Description for existing partner confirmation + * @uiName Existing partner description + * @uiWidget textArea + */ + "descriptionExistingPartner"?: string; + /** + * Description for new partner setup + * @uiName New partner description + * @uiWidget textArea + */ + "descriptionNewPartner"?: string; + /** + * Used to render in another modal. + * @undocumented + */ + "inModal"?: boolean; + /** + * @uiName Missing fields error text + * @uiWidget textArea + */ + "missingFieldsErrorText"?: string; + /** + * Header text when user has no existing partner + * @uiName New partner header + * @uiWidget textArea + */ + "modalHeader"?: string; + /** + * Header text when user has an existing partner + * @uiName Existing partner header + * @uiWidget textArea + */ + "modalHeaderExistingPartner"?: string; + /** + * @uiName Network error text + * @uiWidget textArea + */ + "networkErrorText"?: string; + /** + * @uiName Search country placeholder + */ + "searchCountryPlaceholder"?: string; + /** + * @uiName Search currency placeholder + */ + "searchCurrencyPlaceholder"?: string; + /** + * @undocumented + * @componentState { "title": "New partner", "props": { "states": { "open": true, "isExistingPartner": false } } } + * @componentState { "title": "Existing partner", "props": { "states": { "open": true, "isExistingPartner": true, "countryCode": "US", "currency": "USD" } } } + * @componentState { "title": "Connected (hidden)", "props": { "states": { "open": false } } } + */ + "stateController"?: string; + /** + * @uiName Submit button label + */ + "submitButtonLabel"?: string; + /** + * Support description for existing partner confirmation + * @uiName Existing partner support description + * @uiWidget textArea + */ + "supportDescriptionExistingPartner"?: string; + /** + * The link text that appears in the terms and conditions checkbox + * @uiName Terms and conditions text + * @uiWidget textArea + */ + "termsAndConditionsLabel"?: string; + /** + * The link that appears in the terms and conditions checkbox + * @uiName Terms and conditions link + */ + "termsAndConditionsLink"?: string; + } interface SqmPasswordField { /** * @undocumented @@ -14918,6 +15452,78 @@ declare namespace LocalJSX { * @uiGroup Code Verification Step */ "codeStep_verifyText"?: string; + /** + * @uiName Confirm button label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_confirmButtonLabel"?: string; + /** + * @uiName Country label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_countryLabel"?: string; + /** + * @uiName Currency label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_currencyLabel"?: string; + /** + * @uiName Existing partner description + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_descriptionExistingPartner"?: string; + /** + * @uiName New partner description + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_descriptionNewPartner"?: string; + /** + * @uiName Missing fields error text + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_missingFieldsErrorText"?: string; + /** + * @uiName New partner header + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_modalHeader"?: string; + /** + * @uiName Existing partner header + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_modalHeaderExistingPartner"?: string; + /** + * @uiName Network error text + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_networkErrorText"?: string; + /** + * @uiName Search country placeholder + * @uiGroup Partner Creation Step + */ + "createPartnerStep_searchCountryPlaceholder"?: string; + /** + * @uiName Search currency placeholder + * @uiGroup Partner Creation Step + */ + "createPartnerStep_searchCurrencyPlaceholder"?: string; + /** + * @uiName Submit button label + * @uiGroup Partner Creation Step + */ + "createPartnerStep_submitButtonLabel"?: string; + /** + * @uiName Existing partner support description + * @uiGroup Partner Creation Step + * @uiWidget textArea + */ + "createPartnerStep_supportDescriptionExistingPartner"?: string; /** * @uiName Email input label * @uiGroup Email Verification Step @@ -14958,10 +15564,16 @@ declare namespace LocalJSX { * @uiGroup General Text */ "general_verifyEmailHeader"?: string; + /** + * @uiName General widget header text with partner creation + * @uiGroup General Text + */ + "general_widgetHeaderWithPartnerCreation"?: string; /** * @undocumented * @componentState { "title": "Step 1: Enter email", "props": { "showCode": false }, "dependencies": ["sqm-email-verification"], "uiGroup": "Email Verification Step" } * @componentState { "title": "Step 2: Enter code", "props": { "showCode": true }, "dependencies": ["sqm-code-verification"], "uiGroup": "Code Verification Step" } + * @componentState { "title": "Step 3: Create Partner", "props": { "showPartnerModal": true }, "dependencies": ["sqm-partner-info-modal"], "uiGroup": "Partner Creation Step" } */ "stateController"?: string; } @@ -15023,6 +15635,7 @@ declare namespace LocalJSX { "sqm-navigation-sidebar": SqmNavigationSidebar; "sqm-navigation-sidebar-item": SqmNavigationSidebarItem; "sqm-pagination": SqmPagination; + "sqm-partner-info-modal": SqmPartnerInfoModal; "sqm-password-field": SqmPasswordField; "sqm-payout-button-scroll": SqmPayoutButtonScroll; "sqm-payout-details-card": SqmPayoutDetailsCard; @@ -15157,6 +15770,7 @@ declare module "@stencil/core" { "sqm-navigation-sidebar": LocalJSX.SqmNavigationSidebar & JSXBase.HTMLAttributes; "sqm-navigation-sidebar-item": LocalJSX.SqmNavigationSidebarItem & JSXBase.HTMLAttributes; "sqm-pagination": LocalJSX.SqmPagination & JSXBase.HTMLAttributes; + "sqm-partner-info-modal": LocalJSX.SqmPartnerInfoModal & JSXBase.HTMLAttributes; "sqm-password-field": LocalJSX.SqmPasswordField & JSXBase.HTMLAttributes; "sqm-payout-button-scroll": LocalJSX.SqmPayoutButtonScroll & JSXBase.HTMLAttributes; "sqm-payout-details-card": LocalJSX.SqmPayoutDetailsCard & JSXBase.HTMLAttributes; diff --git a/packages/mint-components/src/components/sqm-partner-info-modal/PartnerInfoModal.stories.tsx b/packages/mint-components/src/components/sqm-partner-info-modal/PartnerInfoModal.stories.tsx new file mode 100644 index 0000000000..59bc2dd3ac --- /dev/null +++ b/packages/mint-components/src/components/sqm-partner-info-modal/PartnerInfoModal.stories.tsx @@ -0,0 +1,173 @@ +import { h } from "@stencil/core"; +import { + PartnerInfoModalView, + PartnerInfoModalViewProps, +} from "./sqm-partner-info-modal-view"; + +export default { + title: "Components/Partner Info Modal", +}; + +const demoCountries = [ + { countryCode: "US", displayName: "United States" }, + { countryCode: "CA", displayName: "Canada" }, + { countryCode: "GB", displayName: "United Kingdom" }, + { countryCode: "AU", displayName: "Australia" }, + { countryCode: "DE", displayName: "Germany" }, + { countryCode: "FR", displayName: "France" }, + { countryCode: "JP", displayName: "Japan" }, +]; + +const demoCurrencies = [ + { currencyCode: "USD", displayName: "US Dollar" }, + { currencyCode: "CAD", displayName: "Canadian Dollar" }, + { currencyCode: "GBP", displayName: "British Pound" }, + { currencyCode: "EUR", displayName: "Euro" }, + { currencyCode: "AUD", displayName: "Australian Dollar" }, +]; + +const noopCallbacks = { + onCountryChange: (e: any) => console.log("Country changed:", e), + onCurrencyChange: (e: any) => console.log("Currency changed:", e), + onCheckboxChange: (e: any) => console.log("Checkbox changed:", e), + setCountrySearch: (v: string) => console.log("Country search:", v), + setCurrencySearch: (v: string) => console.log("Currency search:", v), + onSubmit: () => console.log("Submit"), + onClose: () => console.log("Close"), +}; + +const defaultText = { + modalHeader: "Let's get you ready for rewards", + modalHeaderExistingPartner: "We found an existing account", + descriptionNewPartner: + "Confirm your country and currency now to get your future rewards faster.", + descriptionExistingPartner: + "We noticed you are already an Impact.com partner, please confirm your information.", + supportDescriptionExistingPartner: + "If this is a mistake, please contact Support or sign up for this referral program with a different email.", + countryLabel: "Country", + currencyLabel: "Currency", + submitButtonLabel: "Submit", + confirmButtonLabel: "Confirm", + searchCountryPlaceholder: "Search for a country", + searchCurrencyPlaceholder: "Search for a currency", + bankingCollectionText: "", + allowBankingCollection: + "I have read the {termsAndConditionsLink} and allow impact.com to collect my tax and banking information", + termsAndConditionsLabel: "terms and conditions", + termsAndConditionsLink: + "https://terms.advocate.impact.com/PayoutTermsAndConditions.html", +}; + +const defaultProps: PartnerInfoModalViewProps = { + states: { + open: true, + loading: false, + submitting: false, + isExistingPartner: false, + countryCode: "", + currency: "", + error: "", + success: false, + brandName: "Test Brand", + filteredCountries: demoCountries, + filteredCurrencies: demoCurrencies, + allowBankingCollection: false, + checkboxError: "", + disabled: false, + }, + callbacks: noopCallbacks, + text: defaultText, +}; + +export const NewPartnerEmpty = () => { + return ; +}; + +export const NewPartnerPrefilled = () => { + const props: PartnerInfoModalViewProps = { + ...defaultProps, + states: { + ...defaultProps.states, + countryCode: "US", + currency: "", + }, + }; + return ; +}; + +export const NewPartnerFullySelected = () => { + const props: PartnerInfoModalViewProps = { + ...defaultProps, + states: { + ...defaultProps.states, + countryCode: "US", + currency: "USD", + }, + }; + return ; +}; + +export const ExistingPartnerConfirm = () => { + const props: PartnerInfoModalViewProps = { + ...defaultProps, + states: { + ...defaultProps.states, + isExistingPartner: true, + countryCode: "CA", + currency: "CAD", + }, + }; + return ; +}; + +export const Submitting = () => { + const props: PartnerInfoModalViewProps = { + ...defaultProps, + states: { + ...defaultProps.states, + countryCode: "GB", + currency: "GBP", + submitting: true, + }, + }; + return ; +}; + +export const WithError = () => { + const props: PartnerInfoModalViewProps = { + ...defaultProps, + states: { + ...defaultProps.states, + countryCode: "US", + currency: "USD", + error: + "An error occurred while creating your partner account. Please try again.", + }, + }; + return ; +}; + +export const ValidationError = () => { + const props: PartnerInfoModalViewProps = { + ...defaultProps, + states: { + ...defaultProps.states, + countryCode: "", + currency: "", + error: "Please select both a country and currency.", + }, + }; + return ; +}; + +export const Closed = () => { + const props: PartnerInfoModalViewProps = { + ...defaultProps, + states: { + ...defaultProps.states, + open: false, + }, + }; + return ; +}; diff --git a/packages/mint-components/src/components/sqm-partner-info-modal/readme.md b/packages/mint-components/src/components/sqm-partner-info-modal/readme.md new file mode 100644 index 0000000000..fee2dee457 --- /dev/null +++ b/packages/mint-components/src/components/sqm-partner-info-modal/readme.md @@ -0,0 +1,49 @@ +# sqm-partner-info-modal + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ----------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `allowBankingCollection` | `allow-banking-collection` | Edit the property called terms and conditions text to change what's displayed for {termsAndConditionsLink}. | `string` | `"I have read the {termsAndConditionsLink} and allow impact.com to collect my tax and banking information"` | +| `brandName` | `brand-name` | Brand name shown in the modal header | `string` | `""` | +| `confirmButtonLabel` | `confirm-button-label` | | `string` | `"Confirm"` | +| `countryLabel` | `country-label` | | `string` | `"Country"` | +| `currencyLabel` | `currency-label` | | `string` | `"Currency"` | +| `demoData` | -- | | `{ states?: { open: boolean; loading: boolean; submitting: boolean; isExistingPartner: boolean; countryCode: string; currency: string; error: string; success: boolean; brandName: string; filteredCountries: { countryCode: string; displayName: string; }[]; filteredCurrencies: { currencyCode: string; displayName: string; }[]; allowBankingCollection: boolean; checkboxError: string; disabled: boolean; }; }` | `undefined` | +| `descriptionExistingPartner` | `description-existing-partner` | Description for existing partner confirmation | `string` | `"We found an account with this email on our referral program provider, impact.com. Please confirm your country and currency now to get your future rewards faster."` | +| `descriptionNewPartner` | `description-new-partner` | Description for new partner setup | `string` | `"Confirm your country and currency now to get your future rewards faster."` | +| `inModal` | `in-modal` | Used to render in another modal. | `boolean` | `false` | +| `missingFieldsErrorText` | `missing-fields-error-text` | | `string` | `"Please select both a country and currency."` | +| `modalHeader` | `modal-header` | Header text when user has no existing partner | `string` | `"Let's get you ready for rewards"` | +| `modalHeaderExistingPartner` | `modal-header-existing-partner` | Header text when user has an existing partner | `string` | `"We found an existing account"` | +| `networkErrorText` | `network-error-text` | | `string` | `"An error occurred. Please try again."` | +| `searchCountryPlaceholder` | `search-country-placeholder` | | `string` | `"Search for a country"` | +| `searchCurrencyPlaceholder` | `search-currency-placeholder` | | `string` | `"Search for a currency"` | +| `stateController` | `state-controller` | | `string` | `"{}"` | +| `submitButtonLabel` | `submit-button-label` | | `string` | `"Submit"` | +| `supportDescriptionExistingPartner` | `support-description-existing-partner` | Support description for existing partner confirmation | `string` | `"If this is a mistake, please contact Support or sign up for this referral program with a different email."` | +| `termsAndConditionsLabel` | `terms-and-conditions-label` | The link text that appears in the terms and conditions checkbox | `string` | `"terms and conditions"` | +| `termsAndConditionsLink` | `terms-and-conditions-link` | The link that appears in the terms and conditions checkbox | `string` | `"https://terms.advocate.impact.com/PayoutTermsAndConditions.html"` | + + +## Dependencies + +### Used by + + - [sqm-widget-verification](../sqm-widget-verification) + +### Graph +```mermaid +graph TD; + sqm-widget-verification --> sqm-partner-info-modal + style sqm-partner-info-modal fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/mint-components/src/components/sqm-partner-info-modal/sqm-partner-info-modal-view.tsx b/packages/mint-components/src/components/sqm-partner-info-modal/sqm-partner-info-modal-view.tsx new file mode 100644 index 0000000000..0c0bfaaf9e --- /dev/null +++ b/packages/mint-components/src/components/sqm-partner-info-modal/sqm-partner-info-modal-view.tsx @@ -0,0 +1,266 @@ +import { h } from "@stencil/core"; +import { createStyleSheet } from "../../styling/JSS"; +import { intl } from "../../global/global"; + +export interface PartnerInfoModalViewProps { + states: { + open: boolean; + loading: boolean; + submitting: boolean; + isExistingPartner: boolean; + countryCode: string; + currency: string; + error: string; + success: boolean; + brandName: string; + filteredCountries: { countryCode: string; displayName: string }[]; + filteredCurrencies: { currencyCode: string; displayName: string }[]; + allowBankingCollection: boolean; + checkboxError: string; + disabled: boolean; + }; + callbacks: { + onCountryChange: (e: any) => void; + onCurrencyChange: (e: any) => void; + onCheckboxChange: (e: any) => void; + setCurrencySearch: (c: any) => void; + setCountrySearch: (c: any) => void; + onSubmit: () => void; + onClose: () => void; + }; + text: { + modalHeader: string; + descriptionNewPartner: string; + descriptionExistingPartner: string; + countryLabel: string; + currencyLabel: string; + submitButtonLabel: string; + confirmButtonLabel: string; + searchCountryPlaceholder: string; + searchCurrencyPlaceholder: string; + supportDescriptionExistingPartner: string; + modalHeaderExistingPartner: string; + allowBankingCollection: string; + termsAndConditionsLabel: string; + termsAndConditionsLink: string; + }; +} + +const style = { + Dialog: { + "&::part(panel)": { + maxWidth: "480px", + }, + "&::part(body)": { + padding: "0 var(--sl-spacing-x-large)", + fontSize: "var(--sl-font-size-small)", + overflow: "visible", + }, + "&::part(overlay)": { + background: "var(--sl-overlay-background-color)", + }, + }, + DialogTitle: { + fontSize: "var(--sl-font-size-x-large)", + fontWeight: "600", + padding: "var(--sl-spacing-large) 0 0 0", + margin: "0", + }, + FormFields: { + display: "flex", + flexDirection: "column", + gap: "var(--sl-spacing-medium)", + marginTop: "var(--sl-spacing-large)", + }, + ErrorMessage: { + color: "var(--sqm-danger-color-text, #d32f2f)", + fontSize: "var(--sl-font-size-small)", + marginTop: "var(--sl-spacing-x-small)", + }, + SearchInput: { + "&::part(base)": { + border: "none", + borderBottom: "1px solid var(--sl-color-neutral-300)", + borderRadius: "0", + }, + }, + DescriptionContainer: { + display: "flex", + flexDirection: "column", + gap: "var(--sl-spacing-medium)", + margin: "0", + "& > p": { + margin: "0", + }, + }, + CheckboxWrapper: { + display: "flex", + justifyContent: "flex-start", + flexDirection: "column", + }, + SubmitButton: { + width: "100%", + marginTop: "var(--sl-spacing-large)", + }, +}; + +export function PartnerInfoModalContentView(props: PartnerInfoModalViewProps) { + const { states, callbacks, text } = props; + const sheet = createStyleSheet(style); + const styleString = sheet.toString(); + + const description = states.isExistingPartner ? ( + +

{text.descriptionExistingPartner}

+

{text.supportDescriptionExistingPartner}

+
+ ) : ( +

+ {text.descriptionNewPartner} +

+ ); + + const buttonLabel = states.isExistingPartner + ? text.confirmButtonLabel + : text.submitButtonLabel; + + const bankingCollectionText = intl.formatMessage( + { + id: "bankingCollectionText", + defaultMessage: text.allowBankingCollection, + }, + { + termsAndConditionsLink: ( + + {text.termsAndConditionsLabel} + + ), + }, + ); + + return ( +
+ +
+ {description} + + e.stopPropagation()} + onSl-input={(e: any) => { + callbacks.setCountrySearch(e.target?.value); + }} + /> + {states.filteredCountries?.map((c) => ( + {c.displayName} + ))} + + + + e.stopPropagation()} + onSl-input={(e: any) => + callbacks.setCurrencySearch(e.target?.value) + } + /> + {states.filteredCurrencies?.map((c) => ( + + {c.currencyCode} - {c.displayName} + + ))} + +
+ + {bankingCollectionText} + + {states.checkboxError && ( +

{states.checkboxError}

+ )} +
+
+ {states.error &&

{states.error}

} + + {buttonLabel} + +
+ ); +} + +export function PartnerInfoModalView(props: PartnerInfoModalViewProps) { + const { states, text, callbacks } = props; + const sheet = createStyleSheet(style); + const styleString = sheet.toString(); + + if (!states.open) return
; + + return ( +
+ + { + e.preventDefault(); + }} + onSl-hide={(e: any) => { + if (e.target?.tagName === "SL-DIALOG") { + e.preventDefault(); + } + }} + > +

+ {states.isExistingPartner + ? text.modalHeaderExistingPartner + : text.modalHeader} +

+ +
+
+ ); +} diff --git a/packages/mint-components/src/components/sqm-partner-info-modal/sqm-partner-info-modal.tsx b/packages/mint-components/src/components/sqm-partner-info-modal/sqm-partner-info-modal.tsx new file mode 100644 index 0000000000..b880d2d3db --- /dev/null +++ b/packages/mint-components/src/components/sqm-partner-info-modal/sqm-partner-info-modal.tsx @@ -0,0 +1,269 @@ +import { isDemo } from "@saasquatch/component-boilerplate"; +import { useState, withHooks } from "@saasquatch/stencil-hooks"; +import { Component, Prop, h } from "@stencil/core"; +import deepmerge from "deepmerge"; +import { DemoData } from "../../global/demo"; +import { parseStates } from "../../utils/parseStates"; +import { getProps } from "../../utils/utils"; +import { + PartnerInfoModalContentView, + PartnerInfoModalView, + PartnerInfoModalViewProps, +} from "./sqm-partner-info-modal-view"; +import { + PartnerInfoModalResult, + usePartnerInfoModal, +} from "./usePartnerInfoModal"; + +/** + * @uiName Partner Info Modal + * @exampleGroup Tax and Cash + * @validParents ["sqm-portal-container", "sqm-portal-frame", "div", "sqm-divided-layout", "sqb-program-section", "sqb-conditional-section"] + * @example Partner Info Modal - + */ +@Component({ + tag: "sqm-partner-info-modal", + shadow: true, +}) +export class PartnerInfoModal { + /** + * Brand name shown in the modal header + * + * @uiName Brand name + */ + @Prop() + brandName: string = ""; + + /** + * Header text when user has no existing partner + * + * @uiName New partner header + * @uiWidget textArea + */ + @Prop() + modalHeader: string = "Let's get you ready for rewards"; + + /** + * Header text when user has an existing partner + * + * @uiName Existing partner header + * @uiWidget textArea + */ + @Prop() + modalHeaderExistingPartner: string = "We found an existing account"; + + /** + * Description for new partner setup + * + * @uiName New partner description + * @uiWidget textArea + */ + @Prop() + descriptionNewPartner: string = + "Confirm your country and currency now to get your future rewards faster."; + + /** + * Description for existing partner confirmation + * + * @uiName Existing partner description + * @uiWidget textArea + */ + @Prop() + descriptionExistingPartner: string = + "We found an account with this email on our referral program provider, impact.com. Please confirm your country and currency now to get your future rewards faster."; + + /** + * Support description for existing partner confirmation + * + * @uiName Existing partner support description + * @uiWidget textArea + */ + @Prop() + supportDescriptionExistingPartner: string = + "If this is a mistake, please contact Support or sign up for this referral program with a different email."; + + /** + * Edit the property called terms and conditions text to change what's displayed for {termsAndConditionsLink}. + * @uiName Terms and conditions checkbox + */ + @Prop() allowBankingCollection: string = + "I have read the {termsAndConditionsLink} and allow impact.com to collect my tax and banking information"; + /** + * The link text that appears in the terms and conditions checkbox + * @uiName Terms and conditions text + * @uiWidget textArea + */ + @Prop() termsAndConditionsLabel: string = "terms and conditions"; + /** + * The link that appears in the terms and conditions checkbox + * @uiName Terms and conditions link + */ + @Prop() termsAndConditionsLink: string = + "https://terms.advocate.impact.com/PayoutTermsAndConditions.html"; + /** + * @uiName Country label + */ + @Prop() + countryLabel: string = "Country"; + + /** + * @uiName Currency label + */ + @Prop() + currencyLabel: string = "Currency"; + + /** + * @uiName Submit button label + */ + @Prop() + submitButtonLabel: string = "Submit"; + + /** + * @uiName Confirm button label + */ + @Prop() + confirmButtonLabel: string = "Confirm"; + + /** + * @uiName Search country placeholder + */ + @Prop() + searchCountryPlaceholder: string = "Search for a country"; + + /** + * @uiName Search currency placeholder + */ + @Prop() + searchCurrencyPlaceholder: string = "Search for a currency"; + + /** + * @uiName Network error text + * @uiWidget textArea + */ + @Prop() + networkErrorText: string = "An error occurred. Please try again."; + + /** + * @uiName Missing fields error text + * @uiWidget textArea + */ + @Prop() + missingFieldsErrorText: string = "Please select both a country and currency."; + + /** + * Used to render in another modal. + * @undocumented + */ + @Prop() inModal: boolean = false; + + /** + * @undocumented + * @componentState { "title": "New partner", "props": { "states": { "open": true, "isExistingPartner": false } } } + * @componentState { "title": "Existing partner", "props": { "states": { "open": true, "isExistingPartner": true, "countryCode": "US", "currency": "USD" } } } + * @componentState { "title": "Connected (hidden)", "props": { "states": { "open": false } } } + */ + @Prop() stateController: string = "{}"; + + /** + * @undocumented + * @uiType object + */ + @Prop() demoData?: DemoData; + + constructor() { + withHooks(this); + } + disconnectedCallback() {} + + getTextProps() { + return getProps(this); + } + + render() { + const props = isDemo() + ? useDemoPartnerInfoModal(this) + : usePartnerInfoModal(this); + + if (this.inModal) { + return ; + } + + return ; + } +} + +function useDemoPartnerInfoModal( + props: PartnerInfoModal, +): PartnerInfoModalViewProps { + const [countryCode, setCountryCode] = useState("US"); + const [currency, setCurrency] = useState(""); + const [error, setError] = useState(""); + const [allowBankingCollection, setAllowBankingCollection] = useState(false); + + const parsed = parseStates(props.stateController); + const stateOverride = parsed?.["sqm-partner-info-modal"] || parsed || {}; + + // @ts-ignore + return deepmerge( + { + states: { + brandName: "Test Brand", + open: false, + loading: false, + submitting: false, + isExistingPartner: false, + countryCode, + currency, + error, + success: false, + filteredCountries: [], + filteredCurrencies: [], + allowBankingCollection, + checkboxError: "", + disabled: false, + }, + callbacks: { + onCountryChange: (e: any) => { + const value = e?.detail?.item?.__value; + if (value) { + setCountryCode(value); + setCurrency(""); + } + }, + onCurrencyChange: (e: any) => { + const value = e?.detail?.item?.__value; + if (value) setCurrency(value); + }, + onCheckboxChange: (e: any) => { + setAllowBankingCollection(e.target.checked); + }, + setCountrySearch: () => {}, + setCurrencySearch: () => {}, + onSubmit: () => { + if (!countryCode || !currency) { + setError(props.missingFieldsErrorText); + return; + } + setError(""); + }, + onClose: () => {}, + }, + text: { + modalHeader: props.modalHeader, + descriptionNewPartner: props.descriptionNewPartner, + descriptionExistingPartner: props.descriptionExistingPartner, + countryLabel: props.countryLabel, + currencyLabel: props.currencyLabel, + submitButtonLabel: props.submitButtonLabel, + confirmButtonLabel: props.confirmButtonLabel, + searchCountryPlaceholder: props.searchCountryPlaceholder, + searchCurrencyPlaceholder: props.searchCurrencyPlaceholder, + supportDescriptionExistingPartner: + props.supportDescriptionExistingPartner, + modalHeaderExistingPartner: props.modalHeaderExistingPartner, + }, + }, + props.demoData || stateOverride, + { arrayMerge: (_, a) => a }, + ); +} diff --git a/packages/mint-components/src/components/sqm-partner-info-modal/usePartnerInfoModal.tsx b/packages/mint-components/src/components/sqm-partner-info-modal/usePartnerInfoModal.tsx new file mode 100644 index 0000000000..9272d06c14 --- /dev/null +++ b/packages/mint-components/src/components/sqm-partner-info-modal/usePartnerInfoModal.tsx @@ -0,0 +1,395 @@ +import { + useLocale, + useMutation, + useQuery, + useSetParent, +} from "@saasquatch/component-boilerplate"; +import { useEffect, useMemo, useState } from "@saasquatch/universal-hooks"; +import { gql } from "graphql-request"; +import { PartnerInfoModal } from "./sqm-partner-info-modal"; +import { PartnerInfoModalViewProps } from "./sqm-partner-info-modal-view"; +import { + ConnectPartnerResult, + StartImpactConnectionResult, +} from "../tax-and-cash/sqm-indirect-tax-form/useIndirectTaxForm"; +import { TAX_FORM_UPDATED_EVENT_KEY } from "../tax-and-cash/eventKeys"; +import { VERIFICATION_PARENT_NAMESPACE } from "../sqm-widget-verification/keys"; +import { + GET_FINANCE_NETWORK_SETTINGS, + FinanceNetworkSettingsQuery, +} from "../tax-and-cash/data"; + +// new field under impactConnection:{ resolvedByEmail: boolean } - determines if connection came from managed identity +export const GET_USER_PARTNER_INFO = gql` + query getUserPartnerInfo { + user: viewer { + ... on User { + id + accountId + firstName + lastName + email + countryCode + customFields + impactConnection { + connected + connectionStatus + publisher { + id + countryCode + currency + } + } + } + } + } +`; + +export const GET_COUNTRIES = gql` + query getCountries { + impactPayoutCountries(limit: 1000) { + data { + countryCode + displayName + } + } + } +`; + +export const GET_CURRENCIES = gql` + query currencies($locale: RSLocale) { + currencies(limit: 300) { + data { + displayName(locale: $locale) + currencyCode + } + } + } +`; + +export const CONNECT_PARTNER = gql` + mutation createImpactConnection($vars: ImpactConnectionInput!) { + createImpactConnection(impactConnectionInput: $vars) { + success + validationErrors { + field + message + } + user { + id + accountId + impactConnection { + connected + publisher { + brandedSignup + requiredTaxDocumentType + currentTaxDocument { + type + status + } + } + } + } + } + } +`; + +const START_IMPACT_CONNECTION = gql` + mutation startImpactConnection($vars: ImpactConnectionInput!) { + startImpactConnection(impactConnectionInput: $vars) { + success + validationErrors { + field + message + } + user { + id + accountId + impactConnection { + connected + publisher { + brandedSignup + requiredTaxDocumentType + currentTaxDocument { + type + status + } + } + } + } + } + } +`; + +const GET_BRAND_NAME = gql` + query getTenantSettings { + tenantSettings { + companyName + } + } +`; + +export type TaxCountry = { + countryCode: string; + displayName: string; +}; + +export type CountriesQuery = { + impactPayoutCountries: { + data: TaxCountry[]; + }; +}; + +type TenantSettingsQuery = { + tenantSettings: { + companyName: string; + }; +}; + +export function usePartnerInfoModal( + props: PartnerInfoModal, +): PartnerInfoModalViewProps { + const locale = useLocale(); + + const setVerificationContext = useSetParent(VERIFICATION_PARENT_NAMESPACE); + + const { + data: userData, + loading: userLoading, + refetch, + } = useQuery(GET_USER_PARTNER_INFO, {}); + + const user = userData?.user; + + const { data: currenciesData, loading: currenciesLoading } = useQuery( + GET_CURRENCIES, + { variables: { locale } }, + ); + + const { data: countriesData, loading: countriesLoading } = useQuery( + GET_COUNTRIES, + {}, + ); + + const { data: tenantSettingsData } = useQuery( + GET_BRAND_NAME, + {}, + ); + + const { data: financeNetworkData } = useQuery( + GET_FINANCE_NETWORK_SETTINGS, + { + variables: { filter: {} }, + }, + ); + + const [ + startImpactConnection, + { loading: connectLoading, errors: connectErrors }, + ] = useMutation(START_IMPACT_CONNECTION); + + const [allowBankingCollection, setAllowBankingCollection] = useState(false); + const [checkboxError, setCheckboxError] = useState(""); + // No pre-filled country, use locale to determine countryCode instead + const [countryCode, setCountryCode] = useState( + user?.impactConnection?.publisher?.countryCode || + locale.replace(/^.*_/, ""), + ); + + const [currency, setCurrency] = useState( + user?.impactConnection?.publisher?.currency || "", + ); + + const countries = countriesData?.impactPayoutCountries?.data || []; + + // copied from useTaxAndCash for displaying currencies based on country - could be in helper? + const currencies = useMemo(() => { + const allValidCurrencies = + financeNetworkData?.impactFinanceNetworkSettings?.data?.reduce( + (agg, settings) => { + const currency = currenciesData?.currencies?.data?.find( + (c) => c.currencyCode === settings.currency, + ); + if (!currency) return agg; + if (agg.find((c) => c.currencyCode === settings.currency)) return agg; + if (countryCode && settings.countryCode !== countryCode) return agg; + return [...agg, currency]; + }, + [], + ); + return (allValidCurrencies || []).sort((a, b) => + a.displayName.localeCompare(b.displayName), + ); + }, [financeNetworkData, currenciesData, countryCode]); + + const [countrySearch, setCountrySearch] = useState(""); + const [currencySearch, setCurrencySearch] = useState(""); + const [filteredCountries, setFilteredCountries] = useState( + countriesData?.impactPayoutCountries?.data || [], + ); + const [filteredCurrencies, setFilteredCurrencies] = useState( + currencies || [], + ); + + console.log(userData, "userData partner info modal"); + + const [error, setError] = useState(""); + const [success, setSuccess] = useState(false); + + useEffect(() => { + const publisher = user?.impactConnection?.publisher; + if (!userData || !publisher) return; + setCountryCode(publisher.countryCode); + setCurrency(publisher.currency); + }, [userData, user]); + + useEffect(() => { + if (!countries?.length) return; + if (countrySearch.trim() === "") { + setFilteredCountries(countries || []); + } else { + setFilteredCountries( + countries.filter((c) => + c.displayName.toLowerCase().includes(countrySearch.toLowerCase()), + ) || [], + ); + } + }, [countrySearch, countries]); + + useEffect(() => { + if (!currencies?.length) return; + if (currencySearch.trim() === "") { + setFilteredCurrencies(currencies || []); + } else { + setFilteredCurrencies( + currencies.filter((c) => + c.currencyCode.toLowerCase().includes(currencySearch.toLowerCase()), + ) || [], + ); + } + }, [currencySearch, currencies]); + + const impactConnection = user?.impactConnection; + + function onCountryChange(e: any) { + const value = e.detail?.item?.__value; + if (!value) return; + setCountryCode(value); + setCurrency(""); + setError(""); + } + + function onCurrencyChange(e: any) { + const value = e.detail?.item?.__value; + if (!value) return; + setCurrency(value); + setError(""); + } + + function onCheckboxChange(e: any) { + const checked = e.target.checked; + setAllowBankingCollection(checked); + if (checked) setCheckboxError(""); + } + + async function onSubmit() { + if (!allowBankingCollection) { + setCheckboxError(props.missingFieldsErrorText); + return; + } + if (!countryCode || !currency) { + setError(props.missingFieldsErrorText); + return; + } + setError(""); + setCheckboxError(""); + + try { + const vars = { + user: { + id: user.id, + accountId: user.accountId, + }, + firstName: user.firstName, + lastName: user.lastName, + // phoneNumber: null, + // phoneNumberCountryCode: null, + countryCode, + currency, + }; + + const result = await startImpactConnection({ vars }); + + if (!result || (result as Error)?.message) { + setError(props.networkErrorText); + return; + } + + const connectionResult = (result as StartImpactConnectionResult) + .startImpactConnection; + + console.log( + result, + connectionResult, + "result and connectionResult from creating partner from modal", + ); + + if (!connectionResult?.success) { + const validationMsg = connectionResult?.validationErrors + ?.map((e) => e.message) + .join(". "); + setError(validationMsg || props.networkErrorText); + console.error( + "Failed to create Impact connection:", + connectionResult?.validationErrors, + ); + return; + } + + window.dispatchEvent(new Event(TAX_FORM_UPDATED_EVENT_KEY)); + + await refetch(); + setSuccess(true); + setVerificationContext(true); + } catch (e) { + console.error("Partner creation error:", e); + setError(props.networkErrorText); + } + } + + const showModal = + !success && + !userLoading && + impactConnection?.connectionStatus === "NOT_STARTED"; + + return { + states: { + open: showModal, + loading: userLoading || countriesLoading || currenciesLoading, + submitting: connectLoading, + isExistingPartner: impactConnection?.publisher, + countryCode, + currency, + error, + success, + brandName: tenantSettingsData?.tenantSettings?.companyName || "", + filteredCountries: filteredCountries || [], + filteredCurrencies: filteredCurrencies || [], + allowBankingCollection, + checkboxError, + disabled: userLoading || connectLoading, + }, + callbacks: { + onCountryChange, + onCurrencyChange, + onCheckboxChange, + setCurrencySearch, + setCountrySearch, + onSubmit, + onClose: () => setSuccess(true), + }, + text: props.getTextProps(), + }; +} + +export type PartnerInfoModalResult = ReturnType; diff --git a/packages/mint-components/src/components/sqm-stencilbook/readme.md b/packages/mint-components/src/components/sqm-stencilbook/readme.md index ea883e9018..d1068c7ba1 100644 --- a/packages/mint-components/src/components/sqm-stencilbook/readme.md +++ b/packages/mint-components/src/components/sqm-stencilbook/readme.md @@ -104,6 +104,7 @@ - [sqm-lead-input-field](../sqm-lead-form) - [sqm-lead-dropdown-field](../sqm-lead-form) - [sqm-lead-form](../sqm-lead-form) +- [sqm-widget-verification](../sqm-widget-verification) ### Graph ```mermaid @@ -205,6 +206,7 @@ graph TD; sqm-stencilbook --> sqm-lead-input-field sqm-stencilbook --> sqm-lead-dropdown-field sqm-stencilbook --> sqm-lead-form + sqm-stencilbook --> sqm-widget-verification sqm-form-message --> sqm-skeleton sqm-portal-register --> sqm-form-message sqm-portal-register --> sqm-password-field @@ -283,6 +285,9 @@ graph TD; sqm-payout-status-alert --> sqm-form-message sqm-lead-form --> sqm-form-message sqm-lead-form --> sqm-lead-input-field + sqm-widget-verification --> sqm-partner-info-modal + sqm-widget-verification --> sqm-code-verification + sqm-widget-verification --> sqm-email-verification style sqm-stencilbook fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx b/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx index b52a78359a..f694d18bd8 100644 --- a/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx +++ b/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx @@ -108,6 +108,8 @@ import * as LeadFormDropdownField from "../sqm-lead-form/LeadFormDropdownField.s import * as LeadCheckboxField from "../sqm-lead-form/LeadCheckboxField.stories"; import * as Skeleton from "../sqm-skeleton/Skeleton.stories"; import * as UserInfoFormView from "../tax-and-cash/sqm-user-info-form/UserInfoFormView.stories"; +import * as PartnerInfoModal from "../sqm-partner-info-modal/PartnerInfoModal.stories"; +import * as WidgetVerification from "../sqm-widget-verification/WidgetVerification.stories"; import { ShadowViewAddon } from "../../ShadowViewAddon"; import { CucumberAddon } from "./CucumberAddon"; @@ -221,6 +223,8 @@ const stories = [ TaxAndCashRewardsTable, TaxAndCashReferralTableRewardsCell, TaxAndCashReferralTable, + PartnerInfoModal, + WidgetVerification, ]; /** diff --git a/packages/mint-components/src/components/sqm-text-span/sqm-text-span-view.tsx b/packages/mint-components/src/components/sqm-text-span/sqm-text-span-view.tsx index 472eaa27d4..49b97713c5 100644 --- a/packages/mint-components/src/components/sqm-text-span/sqm-text-span-view.tsx +++ b/packages/mint-components/src/components/sqm-text-span/sqm-text-span-view.tsx @@ -62,7 +62,7 @@ export function TextSpanView(props: TextSpanViewProps, children: VNode) { `; return ( - + + { + e.preventDefault(); + }} + onSl-hide={(e: any) => { + if (e.target?.tagName === "SL-DIALOG") { + e.preventDefault(); + } + }} + > +

{dialogLabel}

+ {renderStepContent()} +
); } @@ -215,7 +383,7 @@ function useDemoWidgetVerificationInternal(props: WidgetVerification) { key === "sqm-widget-verification" ? { ...prev, ...states[key] } : { ...prev, [`${key}_stateController`]: states[key] }, - {} + {}, ); const onVerification = () => { @@ -223,8 +391,14 @@ function useDemoWidgetVerificationInternal(props: WidgetVerification) { }; return deepmerge( - { showCode, onVerification, loading: false }, + { + showCode, + showPartnerModal: false, + onVerification, + onPartnerModalComplete: () => {}, + loading: false, + }, formatted || {}, - { arrayMerge: (_, a) => a } + { arrayMerge: (_, a) => a }, ); } diff --git a/packages/mint-components/src/components/sqm-widget-verification/useWidgetVerification.ts b/packages/mint-components/src/components/sqm-widget-verification/useWidgetVerification.ts index ec7442ce05..0c58961cb3 100644 --- a/packages/mint-components/src/components/sqm-widget-verification/useWidgetVerification.ts +++ b/packages/mint-components/src/components/sqm-widget-verification/useWidgetVerification.ts @@ -25,6 +25,9 @@ const USER_LOOKUP = gql` email emailVerified } + impactConnection { + connected + } } } } @@ -42,6 +45,7 @@ export function useWidgetVerification() { }); const setContext = useSetParent(VERIFICATION_PARENT_NAMESPACE); const [loading, setLoading] = useState(true); + const [showPartnerModal, setShowPartnerModal] = useState(false); const [fetch] = useLazyQuery(USER_LOOKUP); useEffect(() => { @@ -50,8 +54,19 @@ export function useWidgetVerification() { const res = await fetch({}); if (!res || res instanceof Error) throw new Error(); - if (res?.viewer?.emailVerified) setContext(true); - else if (res?.viewer?.managedIdentity?.emailVerified) setContext(true); + // Flow changed to send email -> verify code -> show early partner creation modal + const emailVerified = + res?.viewer?.emailVerified || + res?.viewer?.managedIdentity?.emailVerified; + const isConnected = res?.viewer?.impactConnection?.connected; + + if (isConnected) { + // Partner already created, show widget content + setContext(true); + } else if (emailVerified) { + // Email verified but no partner yet, show partner modal + setShowPartnerModal(true); + } } catch (e) { console.error("Could not fetch user information:", e); } finally { @@ -63,8 +78,13 @@ export function useWidgetVerification() { }, []); const onVerification = () => { + setShowPartnerModal(true); + }; + + const onPartnerModalComplete = () => { + setShowPartnerModal(false); setContext(true); }; - return { showCode, onVerification, loading }; + return { showCode, showPartnerModal, onVerification, onPartnerModalComplete, loading }; } diff --git a/packages/mint-components/src/components/tax-and-cash/data.ts b/packages/mint-components/src/components/tax-and-cash/data.ts index 3f3b78e9e5..6719adc7be 100644 --- a/packages/mint-components/src/components/tax-and-cash/data.ts +++ b/packages/mint-components/src/components/tax-and-cash/data.ts @@ -71,6 +71,7 @@ export const GET_USER = gql` } impactConnection { connected + connectionStatus user { firstName lastName @@ -194,6 +195,7 @@ export type UserQuery = { impactConnection: null | { connected: boolean; user: null | ImpactUser; + connectionStatus: "NOT_STARTED" | "STARTED" | "COMPLETED"; publisher: null | ImpactPublisher; }; }; diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/formDefinitions.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/formDefinitions.tsx index 1b1b7e7406..def9c5cced 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/formDefinitions.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/formDefinitions.tsx @@ -11,6 +11,8 @@ export function getFormMap({ getValidationErrorMessage: (props: { type: "required" | "invalid"; label: string; + errorCode?: string; + fieldName?: string; }) => string; bankCountry?: string; }) { @@ -32,6 +34,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.beneficiaryAccountName?.type, label: props.text.beneficiaryAccountNameLabel, + errorCode: errors?.inputErrors?.beneficiaryAccountName?.errorCode, + fieldName: "beneficiaryAccountName", }), })} > @@ -50,6 +54,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankAccountType?.type, label: props.text.bankAccountTypeLabel, + errorCode: errors?.inputErrors?.bankAccountType?.errorCode, + fieldName: "bankAccountType", }), })} > @@ -77,6 +83,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankAccountNumber?.type, label: props.text.bankAccountNumberLabel, + errorCode: errors?.inputErrors?.bankAccountNumber?.errorCode, + fieldName: "bankAccountNumber", }), })} > @@ -97,6 +105,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankAccountNumber?.type, label: props.text.ibanLabel, + errorCode: errors?.inputErrors?.bankAccountNumber?.errorCode, + fieldName: "bankAccountNumber", }), })} > @@ -117,6 +127,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.swiftCode?.type, label: props.text.swiftCodeLabel, + errorCode: errors?.inputErrors?.swiftCode?.errorCode, + fieldName: "swiftCode", }), })} > @@ -152,6 +164,8 @@ export function getFormMap({ bankCountry, } ), + errorCode: errors?.inputErrors?.routingCode?.errorCode, + fieldName: "routingCode", }), })} > @@ -171,6 +185,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankName?.type, label: props.text.bankNameLabel, + errorCode: errors?.inputErrors?.bankName?.errorCode, + fieldName: "bankName", }), })} > @@ -189,6 +205,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.beneficiaryClassification?.type, label: props.text.classificationLabel, + errorCode: errors?.inputErrors?.beneficiaryClassification?.errorCode, + fieldName: "beneficiaryClassification", }), })} > @@ -222,6 +240,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.beneficiaryTaxPayerId?.type, label: props.text.taxPayerIdLabel, + errorCode: errors?.inputErrors?.taxPayerId?.errorCode, + fieldName: "taxPayerId", }), })} >, @@ -240,6 +260,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.beneficiaryClassification?.type, label: props.text.classificationCPFLabel, + errorCode: errors?.inputErrors?.beneficiaryClassification?.errorCode, + fieldName: "beneficiaryClassification", }), })} > @@ -262,6 +284,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.patronymicName?.type, label: props.text.patronymicNameLabel, + errorCode: errors?.inputErrors?.patronymicName?.errorCode, + fieldName: "patronymicName", }), })} > @@ -280,6 +304,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.voCode?.type, label: props.text.voCodeLabel, + errorCode: errors?.inputErrors?.voCode?.errorCode, + fieldName: "voCode", }), })} > @@ -299,6 +325,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.agencyCode?.type, label: props.text.agencyCodeLabel, + errorCode: errors?.inputErrors?.agencyCode?.errorCode, + fieldName: "agencyCode", }), })} > @@ -318,6 +346,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankAddress?.type, label: props.text.bankAddressLabel, + errorCode: errors?.inputErrors?.bankAddress?.errorCode, + fieldName: "bankAddress", }), })} >, @@ -333,6 +363,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankCity?.type, label: props.text.bankCityLabel, + errorCode: errors?.inputErrors?.bankCity?.errorCode, + fieldName: "bankCity", }), })} >, @@ -348,6 +380,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankState?.type, label: props.text.bankStateLabel, + errorCode: errors?.inputErrors?.bankState?.errorCode, + fieldName: "bankState", }), })} >, @@ -363,6 +397,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankPostalCode?.type, label: props.text.bankPostalCodeLabel, + errorCode: errors?.inputErrors?.bankPostalCode?.errorCode, + fieldName: "bankPostalCode", }), })} >, @@ -382,6 +418,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.branchCode?.type, label: props.text.branchCodeLabel, + errorCode: errors?.inputErrors?.branchCode?.errorCode, + fieldName: "branchCode", }), })} > @@ -400,6 +438,8 @@ export function getFormMap({ helpText: getValidationErrorMessage({ type: errors?.inputErrors?.beneficiaryClassification?.type, label: props.text.classificationLabel, + errorCode: errors?.inputErrors?.beneficiaryClassification?.errorCode, + fieldName: "beneficiaryClassification", }), })} > diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/readme.md b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/readme.md index 53516c5e15..55cc4173c3 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/readme.md +++ b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/readme.md @@ -5,69 +5,89 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `agencyCodeLabel` | `agency-code-label` | | `string` | `"Agency code"` | -| `backButton` | `back-button` | Text for the back button in the form | `string` | `"Back"` | -| `bankAccountNumberLabel` | `bank-account-number-label` | | `string` | `"Bank account number"` | -| `bankAccountTypeLabel` | `bank-account-type-label` | | `string` | `"Bank account type"` | -| `bankAddressLabel` | `bank-address-label` | | `string` | `"Bank address"` | -| `bankCityLabel` | `bank-city-label` | | `string` | `"Bank city"` | -| `bankLocationLabel` | `bank-location-label` | | `string` | `"Bank country location"` | -| `bankNameLabel` | `bank-name-label` | | `string` | `"Bank Name"` | -| `bankPostalCodeLabel` | `bank-postal-code-label` | | `string` | `"Bank postal code"` | -| `bankStateLabel` | `bank-state-label` | | `string` | `"Bank province/state"` | -| `beneficiaryAccountNameDescription` | `beneficiary-account-name-description` | | `string` | `"The beneficiary name of your bank account. Ensure this matches the name on your tax form."` | -| `beneficiaryAccountNameLabel` | `beneficiary-account-name-label` | | `string` | `"Account holder name"` | -| `branchCodeLabel` | `branch-code-label` | | `string` | `"Branch code"` | -| `businessSelectItemLabel` | `business-select-item-label` | One of three options listed for the classification field | `string` | `"Business"` | -| `checkingSelectItemLabel` | `checking-select-item-label` | | `string` | `"Checking"` | -| `classificationCPFLabel` | `classification-c-p-f-label` | | `string` | `"Classification CPF"` | -| `classificationEntityLabel` | `classification-entity-label` | | `string` | `"Classification Entity"` | -| `classificationLabel` | `classification-label` | Label text for the classification input field | `string` | `"Classification"` | -| `continueButton` | `continue-button` | | `string` | `"Save"` | -| `demoData` | -- | | `{ states?: { showVerification: boolean; step?: string; locale?: string; loading: boolean; saveLoading?: boolean; disabled: boolean; saveDisabled: boolean; hideSteps?: boolean; hasPayPal: boolean; hideBanking?: boolean; hidePayPal?: boolean; hideBalanceThreshold?: boolean; hideFixedDay?: boolean; hideBackButton: boolean; feeCap?: string; isPartner: boolean; paymentMethodFeeLabel?: string; loadingError: boolean; formState: BankingInfoFormData & { paymentMethodChecked?: "toBankAccount" \| "toPayPalAccount"; paymentScheduleChecked?: "BALANCE_THRESHOLD" \| "FIXED_DAY"; errors?: { general?: boolean; inputErrors?: { [field: string]: { type: "required" \| "invalid"; }; }; }; }; bitset?: number; currency?: string; thresholds: string[]; countries?: { countryCode: string; displayName: string; }[]; allCountries?: { countryCode: string; displayName: string; }[]; currentPaymentOption?: any; showInputs?: boolean; bankCountry?: string; countrySearch?: string; email?: string; showModal: boolean; }; slots?: { verificationDialogSlot?: VNode; formInputsSlot?: VNode[]; countryInputSlot?: VNode; paymentMethodSlot?: VNode; paymentThresholdSelectSlot?: VNode; paymentFixedDaySelectSlot?: VNode; paypalInputSlot?: VNode; }; refs?: { formRef: any; }; }` | `undefined` | -| `directlyToBankAccount` | `directly-to-bank-account` | | `string` | `"Directly to my bank account"` | -| `eftWithdrawalLabel` | `eft-withdrawal-label` | Default payment method to the participants’ bank account. | `string` | `"EFT withdrawal (free)"` | -| `fieldInvalidError` | `field-invalid-error` | Displayed under a field when it has an invalid entry. | `string` | `"{fieldName} is invalid"` | -| `fieldRequiredError` | `field-required-error` | Displayed under a field that is missing required information. | `string` | `"{fieldName} is required"` | -| `foreignSelectItemLabel` | `foreign-select-item-label` | One of three options listed for the classification field | `string` | `"Foreign"` | -| `formStep` | `form-step` | | `string` | `"Step {step} of {count}"` | -| `fxWireProcessingFeeLabel` | `fx-wire-processing-fee-label` | | `string` | `"FX Wire (Processing Fee {currency}{defaultFxFee}.00)"` | -| `generalErrorDescription` | `general-error-description` | Part of the alert displayed at the top of the page. | `string` | `"Please review your information and try again. If this problem continues, contact our {supportLink}."` | -| `generalErrorTitle` | `general-error-title` | Part of the alert displayed at the top of the page. | `string` | `"There was a problem submitting your information"` | -| `ibanLabel` | `iban-label` | | `string` | `"IBAN"` | -| `individualSelectItemLabel` | `individual-select-item-label` | One of three options listed for the classification field | `string` | `"Individual"` | -| `isPartnerAlertDescription` | `is-partner-alert-description` | Part of the alert displayed at the top of the page if the participant is already a registered partner on impact.com. | `string` | `"If you don’t recognize this referral program provider or believe this is a mistake, please contact our {supportLink} or sign up for this referral program with a different email."` | -| `isPartnerAlertHeader` | `is-partner-alert-header` | Part of the alert displayed at the top of the page if the participant is already a registered partner on impact.com. | `string` | `"An account with this email already exists with our referral program provider, impact.com"` | -| `loadingErrorAlertDescription` | `loading-error-alert-description` | Part of the alert displayed at the top of the page. | `string` | `"Please refresh the page and try again. If this problem continues, contact Support."` | -| `loadingErrorAlertHeader` | `loading-error-alert-header` | Part of the alert displayed at the top of the page. | `string` | `"There was a problem loading your form"` | -| `modalButtonText` | `modal-button-text` | | `string` | `"I understand, update my information"` | -| `modalDescription` | `modal-description` | | `string` | `"Updating payment information places your account and payouts on hold for up to 48 hours while we verify your change. Payments scheduled during the hold period are skipped."` | -| `modalTitle` | `modal-title` | | `string` | `"Important Note"` | -| `patronymicNameLabel` | `patronymic-name-label` | | `string` | `"Patronymic name"` | -| `payPalInputLabel` | `pay-pal-input-label` | Displayed to participants who choose PayPal as their payout method | `string` | `"PayPal email"` | -| `paymentDayFifteenthOfMonthLabelText` | `payment-day-fifteenth-of-month-label-text` | Label text for the payment day select option for the fifteenth of the month | `string` | `"15th of the month"` | -| `paymentDayFirstOfMonthLabelText` | `payment-day-first-of-month-label-text` | One of two payment day options | `string` | `"1st of the month"` | -| `paymentDaySelectLabel` | `payment-day-select-label` | Let the participant choose what day of the month they’ll get paid | `string` | `"Payment Day"` | -| `paymentMethod` | `payment-method` | | `string` | `"Payment method"` | -| `paymentMethodSubtext` | `payment-method-subtext` | | `string` | `"Payouts will be sent from our referral program provider, impact.com."` | -| `paymentSchedule` | `payment-schedule` | | `string` | `"Payment schedule"` | -| `paymentScheduleBalanceThreshold` | `payment-schedule-balance-threshold` | | `string` | `"Pay me when my balance reaches a threshold"` | -| `paymentScheduleFixedDay` | `payment-schedule-fixed-day` | | `string` | `"Pay me on a fixed day of the month"` | -| `paymentThresholdSelectLabel` | `payment-threshold-select-label` | Participant use this field to select the balance at which they want to be paid | `string` | `"Payment Threshold"` | -| `routingCodeLabel` | `routing-code-label` | | `string` | `"{bankCountry, select, AU {BSB number} CA {Routing number} CZ {Bank code} HK {Clearing code} SG {Clearing code} US {ABA routing number} NZ {BSB number} ZA {Bank/Branch number} IN {IFSC} CNY {CNAPS} other {Routing code} }"` | -| `savingsSelectItemLabel` | `savings-select-item-label` | | `string` | `"Savings"` | -| `searchForCountryText` | `search-for-country-text` | Placeholder text displayed in the country search dropdown | `string` | `"Search for country.."` | -| `supportLink` | `support-link` | | `string` | `"support team"` | -| `swiftCodeLabel` | `swift-code-label` | | `string` | `"SWIFT code"` | -| `taxAndPayouts` | `tax-and-payouts` | | `string` | `"Payouts"` | -| `taxAndPayoutsDescription` | `tax-and-payouts-description` | Displayed at the top of the page on all set up steps. | `string` | `"Submit your tax documents and add your banking information to receive your rewards."` | -| `taxPayerIdLabel` | `tax-payer-id-label` | | `string` | `"{country, select, AR {CUIT/CUIL} KR {Classification ID} other { Beneficiary INN } }"` | -| `toPayPalAccount` | `to-pay-pal-account` | | `string` | `"PayPal (2% processing fee capped to {feeCap})"` | -| `verifyEmailDescriptionText` | `verify-email-description-text` | | `string` | `"Verify your email to update your payment settings. Enter the code sent to {email} from our referral provider, impact.com."` | -| `verifyEmailHeaderText` | `verify-email-header-text` | Text for verify email dialog | `string` | `"Verify your email"` | -| `voCodeLabel` | `vo-code-label` | | `string` | `"VO code"` | +| Property | Attribute | Description | Type | Default | +| ------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `agencyCodeError` | `agency-code-error` | Error messages for the agency code field. Supports error codes: empty, alphanumeric, tooShort | `string` | `"{errorCode, select, empty {Agency code is required} alphanumeric {Agency code must contain only letters and numbers} tooShort {Agency code must be at least 5 characters} other {{errorCode}}}"` | +| `agencyCodeLabel` | `agency-code-label` | | `string` | `"Agency code"` | +| `backButton` | `back-button` | Text for the back button in the form | `string` | `"Back"` | +| `bankAccountNumberError` | `bank-account-number-error` | Error messages for the bank account number / IBAN field. Supports error codes: empty, invalidUk, invalid, ibanEmpty, ibanAlphanumeric, ibanInvalid, ibanCountryMismatch | `string` | `"{errorCode, select, empty {Account number is required} invalidUk {Please enter a valid UK account number} invalid {Account number is invalid} ibanEmpty {IBAN is required} ibanAlphanumeric {IBAN must contain only letters and numbers} ibanInvalid {IBAN is invalid} ibanCountryMismatch {UK accounts must use an IBAN starting with GB} other {{errorCode}}}"` | +| `bankAccountNumberLabel` | `bank-account-number-label` | | `string` | `"Bank account number"` | +| `bankAccountTypeError` | `bank-account-type-error` | Error messages for the bank account type field. Supports error codes: empty | `string` | `"{errorCode, select, empty {Bank account type is required} other {{errorCode}}}"` | +| `bankAccountTypeLabel` | `bank-account-type-label` | | `string` | `"Bank account type"` | +| `bankAddressError` | `bank-address-error` | Error messages for the bank address field. Supports error codes: empty | `string` | `"{errorCode, select, empty {Bank address is required} other {{errorCode}}}"` | +| `bankAddressLabel` | `bank-address-label` | | `string` | `"Bank address"` | +| `bankCityError` | `bank-city-error` | Error messages for the bank city field. Supports error codes: empty | `string` | `"{errorCode, select, empty {Bank city is required} other {{errorCode}}}"` | +| `bankCityLabel` | `bank-city-label` | | `string` | `"Bank city"` | +| `bankLocationLabel` | `bank-location-label` | | `string` | `"Bank country location"` | +| `bankNameError` | `bank-name-error` | Error messages for the bank name field. Supports error codes: empty | `string` | `"{errorCode, select, empty {Bank name is required} other {{errorCode}}}"` | +| `bankNameLabel` | `bank-name-label` | | `string` | `"Bank Name"` | +| `bankPostalCodeError` | `bank-postal-code-error` | Error messages for the bank postal code field. Supports error codes: empty | `string` | `"{errorCode, select, empty {Bank postal code is required} other {{errorCode}}}"` | +| `bankPostalCodeLabel` | `bank-postal-code-label` | | `string` | `"Bank postal code"` | +| `bankStateError` | `bank-state-error` | Error messages for the bank province/state field. Supports error codes: empty | `string` | `"{errorCode, select, empty {Bank province/state is required} other {{errorCode}}}"` | +| `bankStateLabel` | `bank-state-label` | | `string` | `"Bank province/state"` | +| `beneficiaryAccountNameDescription` | `beneficiary-account-name-description` | | `string` | `"The beneficiary name of your bank account. Ensure this matches the name on your tax form."` | +| `beneficiaryAccountNameError` | `beneficiary-account-name-error` | Error messages for the beneficiary / account holder name field. Supports error codes: empty, invalidCharacters, numeric, tooLong, nonEnglish, businessNameMismatch, nameMismatch, businessPayeeMismatch, payeeMismatch | `string` | `"{errorCode, select, empty {Account holder name is required} invalidCharacters {Account holder name contains invalid characters} numeric {Account holder name cannot be purely numeric} tooLong {Account holder name must be 70 characters or fewer} nonEnglish {Account holder name must contain only English characters for this currency} businessNameMismatch {Beneficiary name must match the name on your tax document} nameMismatch {Beneficiary name must match the name on your tax document} businessPayeeMismatch {Payee name must match the name on your tax document} payeeMismatch {Payee name must match the name on your tax document} other {{errorCode}}}"` | +| `beneficiaryAccountNameLabel` | `beneficiary-account-name-label` | | `string` | `"Account holder name"` | +| `branchCodeError` | `branch-code-error` | Error messages for the branch code field. Supports error codes: invalid | `string` | `"{errorCode, select, invalid {Branch code is invalid} other {{errorCode}}}"` | +| `branchCodeLabel` | `branch-code-label` | | `string` | `"Branch code"` | +| `branchNameError` | `branch-name-error` | Error messages for the branch name field. Supports error codes: empty | `string` | `"{errorCode, select, empty {Branch name is required} other {{errorCode}}}"` | +| `businessSelectItemLabel` | `business-select-item-label` | One of three options listed for the classification field | `string` | `"Business"` | +| `checkingSelectItemLabel` | `checking-select-item-label` | | `string` | `"Checking"` | +| `classificationCPFLabel` | `classification-c-p-f-label` | | `string` | `"Classification CPF"` | +| `classificationCodeError` | `classification-code-error` | Error messages for the classification code field. Supports error codes: empty, invalidKzt | `string` | `"{errorCode, select, empty {Classification code is required} invalidKzt {Classification code must be exactly 2 characters} other {{errorCode}}}"` | +| `classificationEntityLabel` | `classification-entity-label` | | `string` | `"Classification Entity"` | +| `classificationLabel` | `classification-label` | Label text for the classification input field | `string` | `"Classification"` | +| `continueButton` | `continue-button` | | `string` | `"Save"` | +| `demoData` | -- | | `{ states?: { showVerification: boolean; step?: string; locale?: string; loading: boolean; saveLoading?: boolean; disabled: boolean; saveDisabled: boolean; hideSteps?: boolean; hasPayPal: boolean; hideBanking?: boolean; hidePayPal?: boolean; hideBalanceThreshold?: boolean; hideFixedDay?: boolean; hideBackButton: boolean; feeCap?: string; isPartner: boolean; paymentMethodFeeLabel?: string; loadingError: boolean; formState: BankingInfoFormData & { paymentMethodChecked?: "toBankAccount" \| "toPayPalAccount"; paymentScheduleChecked?: "BALANCE_THRESHOLD" \| "FIXED_DAY"; errors?: { general?: boolean; inputErrors?: { [field: string]: { type: "required" \| "invalid"; errorCode?: string; }; }; }; }; bitset?: number; currency?: string; thresholds: string[]; countries?: { countryCode: string; displayName: string; }[]; allCountries?: { countryCode: string; displayName: string; }[]; currentPaymentOption?: any; showInputs?: boolean; bankCountry?: string; countrySearch?: string; email?: string; showModal: boolean; }; slots?: { verificationDialogSlot?: VNode; formInputsSlot?: VNode[]; countryInputSlot?: VNode; paymentMethodSlot?: VNode; paymentThresholdSelectSlot?: VNode; paymentFixedDaySelectSlot?: VNode; paypalInputSlot?: VNode; }; refs?: { formRef: any; }; }` | `undefined` | +| `directlyToBankAccount` | `directly-to-bank-account` | | `string` | `"Directly to my bank account"` | +| `eftWithdrawalLabel` | `eft-withdrawal-label` | Default payment method to the participants’ bank account. | `string` | `"EFT withdrawal (free)"` | +| `fieldInvalidError` | `field-invalid-error` | Displayed under a field when it has an invalid entry. | `string` | `"{fieldName} is invalid"` | +| `fieldRequiredError` | `field-required-error` | Displayed under a field that is missing required information. | `string` | `"{fieldName} is required"` | +| `foreignSelectItemLabel` | `foreign-select-item-label` | One of three options listed for the classification field | `string` | `"Foreign"` | +| `formStep` | `form-step` | | `string` | `"Step {step} of {count}"` | +| `fxWireProcessingFeeLabel` | `fx-wire-processing-fee-label` | | `string` | `"FX Wire (Processing Fee {currency}{defaultFxFee}.00)"` | +| `generalErrorDescription` | `general-error-description` | Part of the alert displayed at the top of the page. | `string` | `"Please review your information and try again. If this problem continues, contact our {supportLink}."` | +| `generalErrorTitle` | `general-error-title` | Part of the alert displayed at the top of the page. | `string` | `"There was a problem submitting your information"` | +| `ibanLabel` | `iban-label` | | `string` | `"IBAN"` | +| `individualSelectItemLabel` | `individual-select-item-label` | One of three options listed for the classification field | `string` | `"Individual"` | +| `isPartnerAlertDescription` | `is-partner-alert-description` | Part of the alert displayed at the top of the page if the participant is already a registered partner on impact.com. | `string` | `"If you don’t recognize this referral program provider or believe this is a mistake, please contact our {supportLink} or sign up for this referral program with a different email."` | +| `isPartnerAlertHeader` | `is-partner-alert-header` | Part of the alert displayed at the top of the page if the participant is already a registered partner on impact.com. | `string` | `"An account with this email already exists with our referral program provider, impact.com"` | +| `loadingErrorAlertDescription` | `loading-error-alert-description` | Part of the alert displayed at the top of the page. | `string` | `"Please refresh the page and try again. If this problem continues, contact Support."` | +| `loadingErrorAlertHeader` | `loading-error-alert-header` | Part of the alert displayed at the top of the page. | `string` | `"There was a problem loading your form"` | +| `modalButtonText` | `modal-button-text` | | `string` | `"I understand, update my information"` | +| `modalDescription` | `modal-description` | | `string` | `"Updating payment information places your account and payouts on hold for up to 48 hours while we verify your change. Payments scheduled during the hold period are skipped."` | +| `modalTitle` | `modal-title` | | `string` | `"Important Note"` | +| `patronymicNameError` | `patronymic-name-error` | Error messages for the patronymic name field. Supports error codes: empty, alphanumeric | `string` | `"{errorCode, select, empty {Patronymic name is required} alphanumeric {Patronymic name must contain only letters and numbers} other {{errorCode}}}"` | +| `patronymicNameLabel` | `patronymic-name-label` | | `string` | `"Patronymic name"` | +| `payPalInputLabel` | `pay-pal-input-label` | Displayed to participants who choose PayPal as their payout method | `string` | `"PayPal email"` | +| `paymentDayError` | `payment-day-error` | Error messages for the payment day field. Supports error codes: empty, invalid | `string` | `"{errorCode, select, empty {Payment day is required} invalid {Payment day must be the 1st or the 15th} other {{errorCode}}}"` | +| `paymentDayFifteenthOfMonthLabelText` | `payment-day-fifteenth-of-month-label-text` | Label text for the payment day select option for the fifteenth of the month | `string` | `"15th of the month"` | +| `paymentDayFirstOfMonthLabelText` | `payment-day-first-of-month-label-text` | One of two payment day options | `string` | `"1st of the month"` | +| `paymentDaySelectLabel` | `payment-day-select-label` | Let the participant choose what day of the month they’ll get paid | `string` | `"Payment Day"` | +| `paymentMethod` | `payment-method` | | `string` | `"Payment method"` | +| `paymentMethodSubtext` | `payment-method-subtext` | | `string` | `"Payouts will be sent from our referral program provider, impact.com."` | +| `paymentSchedule` | `payment-schedule` | | `string` | `"Payment schedule"` | +| `paymentScheduleBalanceThreshold` | `payment-schedule-balance-threshold` | | `string` | `"Pay me when my balance reaches a threshold"` | +| `paymentScheduleFixedDay` | `payment-schedule-fixed-day` | | `string` | `"Pay me on a fixed day of the month"` | +| `paymentThresholdError` | `payment-threshold-error` | Error messages for the payment threshold field. Supports error codes: empty, invalid | `string` | `"{errorCode, select, empty {Payment threshold is required} invalid {Payment threshold is invalid} other {{errorCode}}}"` | +| `paymentThresholdSelectLabel` | `payment-threshold-select-label` | Participant use this field to select the balance at which they want to be paid | `string` | `"Payment Threshold"` | +| `paypalEmailError` | `paypal-email-error` | Error messages for the PayPal email field. Supports error codes: empty, unsupportedCurrency, invalidEmail, verificationIncomplete | `string` | `"{errorCode, select, empty {PayPal email is required} unsupportedCurrency {PayPal is not supported for this currency} invalidEmail {Please enter a valid email address} verificationIncomplete {PayPal verification is not complete} other {{errorCode}}}"` | +| `routingCodeError` | `routing-code-error` | Error messages for the routing code / sort code / BSB field. Supports error codes: invalidBsb, invalidSortCode, empty, invalid | `string` | `"{errorCode, select, invalidBsb {Please enter a valid BSB number} invalidSortCode {Please enter a valid sort code} empty {Routing number is required} invalid {Routing number is invalid} other {{errorCode}}}"` | +| `routingCodeLabel` | `routing-code-label` | | `string` | `"{bankCountry, select, AU {BSB number} CA {Routing number} CZ {Bank code} HK {Clearing code} SG {Clearing code} US {ABA routing number} NZ {BSB number} ZA {Bank/Branch number} IN {IFSC} CNY {CNAPS} other {Routing code} }"` | +| `savingsSelectItemLabel` | `savings-select-item-label` | | `string` | `"Savings"` | +| `searchForCountryText` | `search-for-country-text` | Placeholder text displayed in the country search dropdown | `string` | `"Search for country.."` | +| `supportLink` | `support-link` | | `string` | `"support team"` | +| `swiftCodeError` | `swift-code-error` | Error messages for the SWIFT / BIC code field. Supports error codes: empty, alphanumeric, invalid | `string` | `"{errorCode, select, empty {SWIFT/BIC code is required} alphanumeric {SWIFT/BIC code must contain only letters and numbers} invalid {SWIFT/BIC code is invalid} other {{errorCode}}}"` | +| `swiftCodeLabel` | `swift-code-label` | | `string` | `"SWIFT code"` | +| `taxAndPayouts` | `tax-and-payouts` | | `string` | `"Payouts"` | +| `taxAndPayoutsDescription` | `tax-and-payouts-description` | Displayed at the top of the page on all set up steps. | `string` | `"Submit your tax documents and add your banking information to receive your rewards."` | +| `taxPayerIdError` | `tax-payer-id-error` | Error messages for the tax payer ID / classification entity field. Supports error codes: empty, emptyAr, emptyKr, alphanumeric, alphanumericAr, alphanumericKr, invalid, invalidAr, invalidKr, invalidKzt, cnpjTooShort, cpfTooShort | `string` | `"{errorCode, select, empty {Tax payer ID is required} emptyAr {CUIT/CUIL is required} emptyKr {Classification ID is required} alphanumeric {Tax payer ID must contain only letters and numbers} alphanumericAr {CUIT/CUIL must contain only letters and numbers} alphanumericKr {Classification ID must contain only letters and numbers} invalid {Tax payer ID is invalid} invalidAr {CUIT/CUIL must be 11 characters} invalidKr {Classification ID length is invalid} invalidKzt {Tax payer ID must be 12 characters for KZT} cnpjTooShort {CNPJ must be at least 14 characters} cpfTooShort {CPF must be at least 11 characters} other {{errorCode}}}"` | +| `taxPayerIdLabel` | `tax-payer-id-label` | | `string` | `"{country, select, AR {CUIT/CUIL} KR {Classification ID} other { Beneficiary INN } }"` | +| `toPayPalAccount` | `to-pay-pal-account` | | `string` | `"PayPal (2% processing fee capped to {feeCap})"` | +| `verifyEmailDescriptionText` | `verify-email-description-text` | | `string` | `"Verify your email to update your payment settings. Enter the code sent to {email} from our referral provider, impact.com."` | +| `verifyEmailHeaderText` | `verify-email-header-text` | Text for verify email dialog | `string` | `"Verify your email"` | +| `voCodeError` | `vo-code-error` | Error messages for the VO code field. Supports error codes: empty, alphanumeric | `string` | `"{errorCode, select, empty {VO code is required} alphanumeric {VO code must contain only letters and numbers} other {{errorCode}}}"` | +| `voCodeLabel` | `vo-code-label` | | `string` | `"VO code"` | ## Dependencies diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form-view.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form-view.tsx index 806eaf1139..3819882246 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form-view.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form-view.tsx @@ -32,6 +32,7 @@ export interface BankingInfoFormViewProps { inputErrors?: { [field: string]: { type: "required" | "invalid"; + errorCode?: string; }; }; }; @@ -105,6 +106,7 @@ export interface BankingInfoFormViewProps { generalTitle: string; generalDescription: string; }; + errorMessages?: { [field: string]: string }; }; refs: { formRef: any; diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form.tsx index 2a674f973d..8d005baa3e 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/sqm-banking-info-form.tsx @@ -350,6 +350,197 @@ export class BankingInfoForm { */ @Prop() modalButtonText: string = "I understand, update my information"; + // ────────────────────────────────────────────────────────────────── + // Per-field validation error messages + // Each prop uses ICU select on {errorCode} to pick the right message. + // Error codes are short frontend keys mapped from the API error codes. + // The `other` branch displays the raw API message directly via {errorCode}, + // which is already human-readable English (e.g. "Invalid Routing Code"). + // ────────────────────────────────────────────────────────────────── + + /** + * Error messages for the beneficiary / account holder name field. + * Supports error codes: empty, invalidCharacters, numeric, tooLong, + * nonEnglish, businessNameMismatch, nameMismatch, businessPayeeMismatch, payeeMismatch + * @uiName Beneficiary account name error + * @uiWidget textArea + */ + @Prop() beneficiaryAccountNameError: string = + "{errorCode, select, empty {Account holder name is required} invalidCharacters {Account holder name contains invalid characters} numeric {Account holder name cannot be purely numeric} tooLong {Account holder name must be 70 characters or fewer} nonEnglish {Account holder name must contain only English characters for this currency} businessNameMismatch {Beneficiary name must match the name on your tax document} nameMismatch {Beneficiary name must match the name on your tax document} businessPayeeMismatch {Payee name must match the name on your tax document} payeeMismatch {Payee name must match the name on your tax document} other {{errorCode}}}"; + + /** + * Error messages for the bank account number / IBAN field. + * Supports error codes: empty, invalidUk, invalid, ibanEmpty, + * ibanAlphanumeric, ibanInvalid, ibanCountryMismatch + * @uiName Bank account number / IBAN error + * @uiWidget textArea + */ + @Prop() bankAccountNumberError: string = + "{errorCode, select, empty {Account number is required} invalidUk {Please enter a valid UK account number} invalid {Account number is invalid} ibanEmpty {IBAN is required} ibanAlphanumeric {IBAN must contain only letters and numbers} ibanInvalid {IBAN is invalid} ibanCountryMismatch {UK accounts must use an IBAN starting with GB} other {{errorCode}}}"; + + /** + * Error messages for the routing code / sort code / BSB field. + * Supports error codes: invalidBsb, invalidSortCode, empty, invalid + * @uiName Routing code error + * @uiWidget textArea + */ + @Prop() routingCodeError: string = + "{errorCode, select, invalidBsb {Please enter a valid BSB number} invalidSortCode {Please enter a valid sort code} empty {Routing number is required} invalid {Routing number is invalid} other {{errorCode}}}"; + + /** + * Error messages for the SWIFT / BIC code field. + * Supports error codes: empty, alphanumeric, invalid + * @uiName SWIFT code error + * @uiWidget textArea + */ + @Prop() swiftCodeError: string = + "{errorCode, select, empty {SWIFT/BIC code is required} alphanumeric {SWIFT/BIC code must contain only letters and numbers} invalid {SWIFT/BIC code is invalid} other {{errorCode}}}"; + + /** + * Error messages for the bank account type field. + * Supports error codes: empty + * @uiName Bank account type error + * @uiWidget textArea + */ + @Prop() bankAccountTypeError: string = + "{errorCode, select, empty {Bank account type is required} other {{errorCode}}}"; + + /** + * Error messages for the bank name field. + * Supports error codes: empty + * @uiName Bank name error + * @uiWidget textArea + */ + @Prop() bankNameError: string = + "{errorCode, select, empty {Bank name is required} other {{errorCode}}}"; + + /** + * Error messages for the tax payer ID / classification entity field. + * Supports error codes: empty, emptyAr, emptyKr, alphanumeric, alphanumericAr, + * alphanumericKr, invalid, invalidAr, invalidKr, invalidKzt, cnpjTooShort, cpfTooShort + * @uiName Tax payer ID error + * @uiWidget textArea + */ + @Prop() taxPayerIdError: string = + "{errorCode, select, empty {Tax payer ID is required} emptyAr {CUIT/CUIL is required} emptyKr {Classification ID is required} alphanumeric {Tax payer ID must contain only letters and numbers} alphanumericAr {CUIT/CUIL must contain only letters and numbers} alphanumericKr {Classification ID must contain only letters and numbers} invalid {Tax payer ID is invalid} invalidAr {CUIT/CUIL must be 11 characters} invalidKr {Classification ID length is invalid} invalidKzt {Tax payer ID must be 12 characters for KZT} cnpjTooShort {CNPJ must be at least 14 characters} cpfTooShort {CPF must be at least 11 characters} other {{errorCode}}}"; + + /** + * Error messages for the patronymic name field. + * Supports error codes: empty, alphanumeric + * @uiName Patronymic name error + * @uiWidget textArea + */ + @Prop() patronymicNameError: string = + "{errorCode, select, empty {Patronymic name is required} alphanumeric {Patronymic name must contain only letters and numbers} other {{errorCode}}}"; + + /** + * Error messages for the VO code field. + * Supports error codes: empty, alphanumeric + * @uiName VO code error + * @uiWidget textArea + */ + @Prop() voCodeError: string = + "{errorCode, select, empty {VO code is required} alphanumeric {VO code must contain only letters and numbers} other {{errorCode}}}"; + + /** + * Error messages for the agency code field. + * Supports error codes: empty, alphanumeric, tooShort + * @uiName Agency code error + * @uiWidget textArea + */ + @Prop() agencyCodeError: string = + "{errorCode, select, empty {Agency code is required} alphanumeric {Agency code must contain only letters and numbers} tooShort {Agency code must be at least 5 characters} other {{errorCode}}}"; + + /** + * Error messages for the bank address field. + * Supports error codes: empty + * @uiName Bank address error + * @uiWidget textArea + */ + @Prop() bankAddressError: string = + "{errorCode, select, empty {Bank address is required} other {{errorCode}}}"; + + /** + * Error messages for the bank city field. + * Supports error codes: empty + * @uiName Bank city error + * @uiWidget textArea + */ + @Prop() bankCityError: string = + "{errorCode, select, empty {Bank city is required} other {{errorCode}}}"; + + /** + * Error messages for the bank province/state field. + * Supports error codes: empty + * @uiName Bank province/state error + * @uiWidget textArea + */ + @Prop() bankStateError: string = + "{errorCode, select, empty {Bank province/state is required} other {{errorCode}}}"; + + /** + * Error messages for the bank postal code field. + * Supports error codes: empty + * @uiName Bank postal code error + * @uiWidget textArea + */ + @Prop() bankPostalCodeError: string = + "{errorCode, select, empty {Bank postal code is required} other {{errorCode}}}"; + + /** + * Error messages for the branch code field. + * Supports error codes: invalid + * @uiName Branch code error + * @uiWidget textArea + */ + @Prop() branchCodeError: string = + "{errorCode, select, invalid {Branch code is invalid} other {{errorCode}}}"; + + /** + * Error messages for the branch name field. + * Supports error codes: empty + * @uiName Branch name error + * @uiWidget textArea + */ + @Prop() branchNameError: string = + "{errorCode, select, empty {Branch name is required} other {{errorCode}}}"; + + /** + * Error messages for the classification code field. + * Supports error codes: empty, invalidKzt + * @uiName Classification code error + * @uiWidget textArea + */ + @Prop() classificationCodeError: string = + "{errorCode, select, empty {Classification code is required} invalidKzt {Classification code must be exactly 2 characters} other {{errorCode}}}"; + + /** + * Error messages for the PayPal email field. + * Supports error codes: empty, unsupportedCurrency, invalidEmail, verificationIncomplete + * @uiName PayPal email error + * @uiWidget textArea + */ + @Prop() paypalEmailError: string = + "{errorCode, select, empty {PayPal email is required} unsupportedCurrency {PayPal is not supported for this currency} invalidEmail {Please enter a valid email address} verificationIncomplete {PayPal verification is not complete} other {{errorCode}}}"; + + /** + * Error messages for the payment threshold field. + * Supports error codes: empty, invalid + * @uiName Payment threshold error + * @uiWidget textArea + */ + @Prop() paymentThresholdError: string = + "{errorCode, select, empty {Payment threshold is required} invalid {Payment threshold is invalid} other {{errorCode}}}"; + + /** + * Error messages for the payment day field. + * Supports error codes: empty, invalid + * @uiName Payment day error + * @uiWidget textArea + */ + @Prop() paymentDayError: string = + "{errorCode, select, empty {Payment day is required} invalid {Payment day must be the 1st or the 15th} other {{errorCode}}}"; + /** * @undocumented * @uiType object @@ -373,6 +564,28 @@ export class BankingInfoForm { loadingErrorAlertDescription: props.loadingErrorAlertDescription, loadingErrorAlertHeader: props.loadingErrorAlertHeader, }, + errorMessages: { + beneficiaryAccountName: props.beneficiaryAccountNameError, + bankAccountNumber: props.bankAccountNumberError, + routingCode: props.routingCodeError, + swiftCode: props.swiftCodeError, + bankAccountType: props.bankAccountTypeError, + bankName: props.bankNameError, + taxPayerId: props.taxPayerIdError, + patronymicName: props.patronymicNameError, + voCode: props.voCodeError, + agencyCode: props.agencyCodeError, + bankAddress: props.bankAddressError, + bankCity: props.bankCityError, + bankState: props.bankStateError, + bankPostalCode: props.bankPostalCodeError, + branchCode: props.branchCodeError, + branchName: props.branchNameError, + beneficiaryClassification: props.classificationCodeError, + paypalEmailAddress: props.paypalEmailError, + paymentThreshold: props.paymentThresholdError, + paymentDay: props.paymentDayError, + }, }; } @@ -389,10 +602,32 @@ export class BankingInfoForm { function getValidationErrorMessage({ type, label, + errorCode, + fieldName, }: { type: "required" | "invalid"; label: string; + errorCode?: string; + fieldName?: string; }) { + // If we have a specific error code from the API, try to use + // the per-field ICU error message template for a rich message + if (type === "invalid" && errorCode && fieldName) { + const errorTemplate = props.text.errorMessages?.[fieldName]; + if (errorTemplate) { + return intl.formatMessage( + { + id: `fieldError-${fieldName}-${errorCode}`, + defaultMessage: errorTemplate, + }, + { + errorCode, + fieldName: label, + } + ); + } + } + if (type === "required") { return intl.formatMessage( { @@ -525,6 +760,8 @@ export class BankingInfoForm { helpText: getValidationErrorMessage({ type: errors?.inputErrors?.bankCountry?.type, label: props.text.bankLocationLabel, + errorCode: errors?.inputErrors?.bankCountry?.errorCode, + fieldName: "bankCountry", }), })} > @@ -578,6 +815,8 @@ export class BankingInfoForm { helpText: getValidationErrorMessage({ type: errors?.inputErrors?.paymentThreshold?.type, label: props.text.paymentThresholdSelectLabel, + errorCode: errors?.inputErrors?.paymentThreshold?.errorCode, + fieldName: "paymentThreshold", }), })} > @@ -601,6 +840,8 @@ export class BankingInfoForm { helpText: getValidationErrorMessage({ type: errors?.inputErrors?.paymentDay?.type, label: props.text.paymentDaySelectLabel, + errorCode: errors?.inputErrors?.paymentDay?.errorCode, + fieldName: "paymentDay", }), })} > @@ -629,6 +870,10 @@ export class BankingInfoForm { type: props.states.formState?.errors?.inputErrors ?.paypalEmailAddress?.type, label: props.text.payPalInputLabel, + errorCode: + props.states.formState?.errors?.inputErrors + ?.paypalEmailAddress?.errorCode, + fieldName: "paypalEmailAddress", }), })} > diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/useBankingInfoForm.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/useBankingInfoForm.tsx index 09a1cb7bfd..f8303c24ad 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/useBankingInfoForm.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-banking-info-form/useBankingInfoForm.tsx @@ -9,7 +9,7 @@ import { } from "@saasquatch/component-boilerplate"; import { useEffect, useRef, useState } from "@saasquatch/universal-hooks"; import { gql } from "graphql-request"; -import JSONPointer, { set } from "jsonpointer"; +import JSONPointer from "jsonpointer"; import { intl } from "../../../global/global"; import { VERIFICATION_EVENT_KEY } from "../../sqm-widget-verification/keys"; import { @@ -49,6 +49,115 @@ const ACH_PAYMENT_METHOD = 3; const WIRE_PAYMENT_METHOD = 5; const PAYPAL_PAYMENT_METHOD = 7; +/** + * Maps GraphQL validation error field names to form field names. + */ +const API_FIELD_TO_FORM_FIELD: Record = { + // bankProvinceState → form uses bankState + bankProvinceState: "bankState", +}; + +/** + * Maps Impact API error code paths (from validationErrors[].errorPath) to short, + * readable frontend error codes used in the ICU select props. + */ +const API_ERROR_PATH_TO_FRONTEND: Record = { + // Beneficiary account name + "withdrawal.settings.error.empty_beneficiaryname": "empty", + "withdrawal.settings.error.invalid_character_beneficiaryname": + "invalidCharacters", + "withdrawal.settings.error.numeric_beneficiaryname": "numeric", + "withdrawal_settings.error.beneficiaryname.size": "tooLong", + "withdrawal.settings.error.non_english_beneficiaryname": "nonEnglish", + "withdrawal_settings.error.business_beneficiaryname_match": + "businessNameMismatch", + "withdrawal_settings.error.beneficiaryname_match": "nameMismatch", + "withdrawal_settings.error.business_checkpayeename_match": + "businessPayeeMismatch", + "withdrawal_settings.error.checkpayeename_match": "payeeMismatch", + + // Bank account number + "withdrawal.settings.error.accountnumber.empty": "empty", + "withdrawal.settings.error.accountnumber.uk": "invalidUk", + "withdrawal.settings.error.bankaccount.invalid": "invalid", + + // IBAN + "withdrawal.settings.error.iban": "ibanEmpty", + "withdrawal.settings.error.iban.alphanumeric": "ibanAlphanumeric", + "withdrawal.settings.error.iban.invalid": "ibanInvalid", + "withdrawal.settings.error.iban.uk.country.mismatch": "ibanCountryMismatch", + + // Routing code + "withdrawal.settings.error.bsbNumber": "invalidBsb", + "withdrawal.settings.error.sortcode": "invalidSortCode", + "withdrawal.settings.error.routingNumber": "empty", + "withdrawal.settings.error.routingcode": "invalid", + + // SWIFT / BIC + "withdrawal.settings.error.bic": "empty", + "withdrawal.settings.error.bic.alphanumeric": "alphanumeric", + "withdrawal.settings.error.bic.invalid": "invalid", + + // Bank account type + "global.error.invalid.accounttype": "empty", + + // Bank name + "withdrawal.settings.error.bankName": "empty", + + // Tax payer ID + "withdrawal.settings.error.taxPayerId": "empty", + "withdrawal.settings.error.taxPayerId.ar": "emptyAr", + "withdrawal.settings.error.taxPayerId.kr": "emptyKr", + "withdrawal.settings.error.taxPayerId.alphanumeric": "alphanumeric", + "withdrawal.settings.error.taxPayerId.alphanumeric.ar": "alphanumericAr", + "withdrawal.settings.error.taxPayerId.alphanumeric.kr": "alphanumericKr", + "withdrawal.settings.error.taxPayerId.invalid": "invalid", + "withdrawal.settings.error.taxPayerId.invalid.ar": "invalidAr", + "withdrawal.settings.error.taxPayerId.invalid.kr": "invalidKr", + "withdrawal.settings.error.taxPayerId.invalid.kzt": "invalidKzt", + "withdrawal.settings.error.taxPayerId.cnpj": "cnpjTooShort", + "withdrawal.settings.error.taxPayerId.cpf": "cpfTooShort", + + // Patronymic name + "withdrawal.settings.error.patronymicName": "empty", + "withdrawal.settings.error.patronymicName.alphanumeric": "alphanumeric", + + // VO code + "withdrawal.settings.error.voCode": "empty", + "withdrawal.settings.error.voCode.alphanumeric": "alphanumeric", + + // Agency code + "withdrawal.settings.error.agencyCode": "empty", + "withdrawal.settings.error.agencyCode.alphanumeric": "alphanumeric", + "withdrawal.settings.error.agencyCode.length": "tooShort", + + // Bank address fields + "withdrawal.settings.error.bankAddress": "empty", + "withdrawal.settings.error.bankCity": "empty", + "withdrawal.settings.error.bankProvinceState": "empty", + "withdrawal.settings.error.bankPostalCode": "empty", + + // Branch code / name + "withdrawal.settings.error.branchCode": "invalid", + "withdrawal.settings.error.branchName": "empty", + + // Classification code + "withdrawal.settings.error.classificationCode.invalid": "empty", + "withdrawal.settings.error.classificationCode.invalid.kzt": "invalidKzt", + + // PayPal + "payment.error.email": "empty", + "payment.error.paypal_not_supported": "unsupportedCurrency", + "payment.error.email.invalid": "invalidEmail", + "payment.error.paypal_verification_incomplete": "verificationIncomplete", + + // Payment schedule + "payment.error.no_threshold": "empty", + "payment.error.invalid_threshold": "invalid", + "payment.error.no_dayOfMonth": "empty", + "payment.error.invalid_dayOfMonth": "invalid", +}; + export type BankingInfoFormData = { // Fields that are auto-filled bankCountry?: string; @@ -101,7 +210,11 @@ export function getFormInputs({ bitset, formMap }) { type SetImpactPublisherWithdrawalSettingsResult = { setImpactPublisherWithdrawalSettings: { success: boolean; - validationErrors: { field: string; message: string }[]; + validationErrors: { + field: string; + message: string; + errorPath: string; + }[]; }; }; type SetImpactPublisherWithdrawalSettingsInput = { @@ -120,7 +233,11 @@ type UpdateImpactPublisherWithdrawalSettingsInput = type UpdateImpactPublisherWithdrawalSettingsResult = { updateImpactPublisherWithdrawalSettings: { success: boolean; - validationErrors: { field: string; message: string }[]; + validationErrors: { + field: string; + message: string; + errorPath: string; + }[]; }; }; @@ -135,6 +252,7 @@ const SAVE_WITHDRAWAL_SETTINGS = gql` validationErrors { field message + code } } } @@ -151,6 +269,7 @@ const UPDATE_WITHDRAWAL_SETTINGS = gql` validationErrors { field message + code } } } @@ -176,7 +295,7 @@ function parseImpactThreshold(threshold: string) { } export function useBankingInfoForm( - props: BankingInfoForm + props: BankingInfoForm, ): BankingInfoFormViewProps { const host = useHost(); const locale = useLocale(); @@ -193,7 +312,7 @@ export function useBankingInfoForm( loading: paymentOptionsLoading, errors: paymentOptionsError, } = useParentQueryValue( - FINANCE_NETWORK_SETTINGS_NAMESPACE + FINANCE_NETWORK_SETTINGS_NAMESPACE, ); const { data: userData, @@ -202,11 +321,11 @@ export function useBankingInfoForm( } = useParentQueryValue(USER_QUERY_NAMESPACE); const [saveWithdrawalSettings] = useMutation( - SAVE_WITHDRAWAL_SETTINGS + SAVE_WITHDRAWAL_SETTINGS, ); const [updateWithdrawalSettings] = useMutation( - UPDATE_WITHDRAWAL_SETTINGS + UPDATE_WITHDRAWAL_SETTINGS, ); const [showVerification, setShowVerification] = useState(false); @@ -244,14 +363,14 @@ export function useBankingInfoForm( { currency: currency, defaultFxFee: currentPaymentOption?.defaultFxFee || 0, - } + }, ), }; const paymentMethodFeeLabel = paymentMethodFeeMap[currentPaymentOption?.defaultFinancePaymentMethodId]; const hasPayPal = !!paymentOptions?.find( - (option) => option.defaultFinancePaymentMethodId === PAYPAL_PAYMENT_METHOD + (option) => option.defaultFinancePaymentMethodId === PAYPAL_PAYMENT_METHOD, ); const paymentMethodChecked = !hasPayPal @@ -282,7 +401,7 @@ export function useBankingInfoForm( paypalEmailAddress: withdrawalSettings.paypalEmailAddress, paymentSchedulingType: withdrawalSettings.paymentSchedulingType, paymentThreshold: parseImpactThreshold( - withdrawalSettings.paymentThreshold + withdrawalSettings.paymentThreshold, ), paymentDay: withdrawalSettings.paymentDay, }; @@ -305,7 +424,7 @@ export function useBankingInfoForm( setPaymentMethodChecked( initialData.paymentMethod === "PAYPAL" ? "toPayPalAccount" - : "toBankAccount" + : "toBankAccount", ); setCurrentPaymentOption(currentPaymentOption); setPaymentScheduleChecked(initialData.paymentSchedulingType); @@ -319,8 +438,8 @@ export function useBankingInfoForm( } else { setFilteredCountries( countries.filter((c) => - c.displayName.toLowerCase().includes(countrySearch.toLowerCase()) - ) || [] + c.displayName.toLowerCase().includes(countrySearch.toLowerCase()), + ) || [], ); } }, [countrySearch, countries]); @@ -390,15 +509,19 @@ export function useBankingInfoForm( const mappedValidationErrors = validationErrors?.reduce( (agg, error) => { + const formField = + API_FIELD_TO_FORM_FIELD[error.field] || error.field; + const errorCode = + API_ERROR_PATH_TO_FRONTEND[error.errorPath] || error.errorPath; return { ...agg, - - [error.field]: { + [formField]: { type: "invalid", + errorCode, }, }; }, - {} + {}, ); setErrors({ @@ -469,12 +592,12 @@ export function useBankingInfoForm( new CustomEvent(VERIFICATION_EVENT_KEY, { detail: { token }, bubbles: false, - }) + }), ); }; function setPaymentMethodChecked( - paymentMethod: "toBankAccount" | "toPayPalAccount" + paymentMethod: "toBankAccount" | "toPayPalAccount", ) { _setPaymentMethodChecked(paymentMethod); @@ -486,7 +609,7 @@ export function useBankingInfoForm( setCurrentPaymentOption(currentPaymentOption); } else if (paymentMethod === "toBankAccount") { const currentPaymentOption = paymentOptions?.find( - (paymentOption) => paymentOption.countryCode === formState.bankCountry + (paymentOption) => paymentOption.countryCode === formState.bankCountry, ); setCurrentPaymentOption(currentPaymentOption); } diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-indirect-tax-form/useIndirectTaxForm.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-indirect-tax-form/useIndirectTaxForm.tsx index b7d9910d3e..e76ab35cf2 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-indirect-tax-form/useIndirectTaxForm.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-indirect-tax-form/useIndirectTaxForm.tsx @@ -55,6 +55,14 @@ export type ConnectPartnerResult = { } | null; }; }; + +export type StartImpactConnectionResult = { + startImpactConnection: ConnectPartnerResult["createImpactConnection"]; +}; + +export type CompletePartnerResult = { + completeImpactConnection: ConnectPartnerResult["createImpactConnection"]; +}; type ImpactConnectionInput = { user: { id: string; @@ -104,6 +112,33 @@ export const CONNECT_PARTNER = gql` } `; +export const COMPLETE_PARTNER = gql` + mutation completeImpactConnection($vars: ImpactConnectionInput!) { + completeImpactConnection(impactConnectionInput: $vars) { + success + validationErrors { + field + message + } + user { + id + accountId + impactConnection { + connected + publisher { + brandedSignup + requiredTaxDocumentType + currentTaxDocument { + type + status + } + } + } + } + } + } +`; + function getOption(countries: TaxCountry[] | undefined, countryCode: string) { if (!countries) return; @@ -127,6 +162,10 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { connectImpactPartner, { loading: connectLoading, errors: connectErrors }, ] = useMutation(CONNECT_PARTNER); + const [ + completeImpactPartner, + { loading: completeLoading, errors: completeErrors }, + ] = useMutation(COMPLETE_PARTNER); const userForm = useParentValue(USER_FORM_CONTEXT_NAMESPACE); const { data: userData, @@ -141,9 +180,9 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { const _countries = useMemo( () => _countriesRes?.impactPayoutCountries?.data?.map((country) => - getCountryObj({ countryCode: country.countryCode, locale: intlLocale }) + getCountryObj({ countryCode: country.countryCode, locale: intlLocale }), ), - [_countriesRes?.impactPayoutCountries?.data] + [_countriesRes?.impactPayoutCountries?.data], ); const [loading, setLoading] = useState(false); @@ -168,7 +207,7 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { const _option = getOption( _countries, - publisher.taxInformation.indirectTaxCountryCode + publisher.taxInformation.indirectTaxCountryCode, ); setOption(_option); }, [publisher, _countries]); @@ -179,8 +218,8 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { } else { setFilteredCountries( _countries?.filter((c) => - c.displayName.toLowerCase().includes(countrySearch.toLowerCase()) - ) || [] + c.displayName.toLowerCase().includes(countrySearch.toLowerCase()), + ) || [], ); } }, [countrySearch, _countries]); @@ -244,16 +283,29 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { withholdingTaxId: formData.subRegionTaxNumber, } as ImpactConnectionInput; - const result = await connectImpactPartner({ - vars, - }); + //AL: TODO completePartnerMutation might change + let result = null; + let connectionResult; + if (userData?.user?.impactConnection?.connected) { + result = await completeImpactPartner({ + vars, + }); + connectionResult = (result as CompletePartnerResult) + ?.completeImpactConnection; + } else { + result = await connectImpactPartner({ + vars, + }); + connectionResult = (result as ConnectPartnerResult) + ?.createImpactConnection; + } if (!result || (result as Error)?.message) throw new Error(); - if (!(result as ConnectPartnerResult).createImpactConnection?.success) { + if (!connectionResult?.success) { // Output backend errors to console for now console.error( "Failed to create Impact connection: ", - (result as ConnectPartnerResult).createImpactConnection.validationErrors + connectionResult?.validationErrors, ); throw new Error(); @@ -261,8 +313,7 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { await refetch(); - const resultPublisher = (result as ConnectPartnerResult) - .createImpactConnection?.user?.impactConnection?.publisher; + const resultPublisher = connectionResult?.user?.impactConnection?.publisher; const hasValidCurrentDocument = validTaxDocument(resultPublisher?.requiredTaxDocumentType) && @@ -308,9 +359,8 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { setLoading(true); try { - const { resultPublisher, hasValidCurrentDocument } = await connectPartner( - formData - ); + const { resultPublisher, hasValidCurrentDocument } = + await connectPartner(formData); if ( resultPublisher?.requiredTaxDocumentType && @@ -342,8 +392,9 @@ export function useIndirectTaxForm(props: IndirectTaxForm) { states: { step: step?.replace("/", ""), hideSteps: context.hideSteps, - disabled: loading || countriesLoading || connectLoading, - loading: loading || connectLoading || countriesLoading, + disabled: + loading || countriesLoading || connectLoading || completeLoading, + loading: loading || connectLoading || countriesLoading || completeLoading, isPartner: !!userData?.user?.impactConnection?.publisher, loadingError: !!userError?.message, formState: { diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash-dashboard/useTaxAndCashDashboard.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash-dashboard/useTaxAndCashDashboard.tsx index cb43daf717..3557aa2abe 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash-dashboard/useTaxAndCashDashboard.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash-dashboard/useTaxAndCashDashboard.tsx @@ -62,7 +62,7 @@ function getIndirectTaxType(taxInformation: ImpactPublisher["taxInformation"]) { if (taxInformation?.indirectTaxRegion) { const standardRegion = taxInformation.indirectTaxRegion.replace("_", ""); const taxType = regions.find( - (r) => r.regionCode === standardRegion + (r) => r.regionCode === standardRegion, )?.taxType; if (taxType) return taxType; @@ -78,7 +78,7 @@ function getIndirectTaxType(taxInformation: ImpactPublisher["taxInformation"]) { } export const useTaxAndCashDashboard = ( - props: TaxAndCashDashboard + props: TaxAndCashDashboard, ): Omit => { const setStep = useSetParent(TAX_CONTEXT_NAMESPACE); const setContext = useSetParent(TAX_FORM_CONTEXT_NAMESPACE); @@ -91,7 +91,7 @@ export const useTaxAndCashDashboard = ( const { data: taxSettingRes } = useQuery( GET_TAX_SETTING, - {} + {}, ); const locale = useLocale(); @@ -147,7 +147,7 @@ export const useTaxAndCashDashboard = ( }; const provinceName = INDIRECT_TAX_PROVINCES.find( - (p) => p.regionCode === publisher?.taxInformation?.indirectTaxRegion + (p) => p.regionCode === publisher?.taxInformation?.indirectTaxRegion, )?.displayName; const payoutStatus = data ? getStatus(data) : null; @@ -179,7 +179,7 @@ export const useTaxAndCashDashboard = ( province: provinceName, country: getCountryName( publisher?.taxInformation?.indirectTaxCountryCode, - locale + locale, ), notRegistered: !publisher?.taxInformation?.indirectTaxId, noFormNeeded: !documentType, diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash/useTaxAndCash.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash/useTaxAndCash.tsx index 2d3cd65de2..d5fae0a6d8 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash/useTaxAndCash.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-tax-and-cash/useTaxAndCash.tsx @@ -45,6 +45,13 @@ function getCurrentStep(user: UserQuery["user"]) { brandedSignup, payoutsAccount, } = user.impactConnection.publisher; + console.log( + user.impactConnection, + "publisher data in useTaxAndCash to determine step new new", + ); + if (user.impactConnection.connectionStatus === "STARTED") { + return "/1"; + } // If they do have a required document, look at current document if (requiredTaxDocumentType && !currentTaxDocument) { @@ -97,7 +104,7 @@ export function useTaxAndCash() { { namespace: CURRENCIES_NAMESPACE, initialValue: [], - } + }, ); const [_countriesContext, _setCountriesContext] = useParentState< @@ -158,7 +165,7 @@ export function useTaxAndCash() { financeNetworkData?.impactFinanceNetworkSettings?.data?.reduce( (agg, settings) => { const currency = currenciesData?.currencies?.data?.find( - (currency) => currency.currencyCode === settings.currency + (currency) => currency.currencyCode === settings.currency, ); // Currency not in supported list if (!currency) return agg; @@ -176,7 +183,7 @@ export function useTaxAndCash() { return [...agg, currency]; }, - [] + [], ); return allValidCurrencies; }, [financeNetworkData, countryCode]); @@ -194,9 +201,9 @@ export function useTaxAndCash() { new Set( paymentOptions ?.map((option) => option.countryCode) - .filter((value) => value) + .filter((value) => value), ), - [paymentOptions] + [paymentOptions], ); const _topCountries = ["CA", "GB", "US"]; @@ -205,7 +212,7 @@ export function useTaxAndCash() { () => Array.from(availableCountries) .map((countryCode) => - getCountryObj({ countryCode, locale: intlLocale }) + getCountryObj({ countryCode, locale: intlLocale }), ) .sort(sortByName) .reduce((prev, countryObj) => { @@ -213,7 +220,7 @@ export function useTaxAndCash() { return [countryObj, ...prev]; return [...prev, countryObj]; }, []), - [availableCountries] + [availableCountries], ); useEffect(() => { diff --git a/packages/mint-components/src/components/tax-and-cash/sqm-user-info-form/sqm-user-info-form-view.tsx b/packages/mint-components/src/components/tax-and-cash/sqm-user-info-form/sqm-user-info-form-view.tsx index 07897c630f..723dcf0574 100644 --- a/packages/mint-components/src/components/tax-and-cash/sqm-user-info-form/sqm-user-info-form-view.tsx +++ b/packages/mint-components/src/components/tax-and-cash/sqm-user-info-form/sqm-user-info-form-view.tsx @@ -413,7 +413,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { {text.termsAndConditionsLabel} ), - } + }, ); let regionLabel = undefined; @@ -431,7 +431,14 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { regionLabel = text.state; } + // when creating an impact connection without sending phoneNumber data, the impactAPI defaults the value to "000000" and the phoneNumberCountryCode to "DZ" function isDisabledPartnerInput(field: string) { + if ( + data.partnerData?.phoneNumber === "0000000" && + (field === "phoneNumber" || field === "phoneNumberCountryCode") + ) { + return false; + } return states.isPartner && !!data.partnerData?.[field]; } @@ -469,7 +476,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { {text.supportLink} ), - } + }, )}

@@ -489,7 +496,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { id: "formStep", defaultMessage: text.formStep, }, - { step: states.step, count: FORM_STEPS } + { step: states.step, count: FORM_STEPS }, )}

)} @@ -518,13 +525,13 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { {text.supportLink} ), - } + }, )}

)} - - {(states.isPartner || states.isUser) && ( + {/* AL: Don't need to show this anymore due to early partner creation */} + {/* {(states.isPartner || states.isUser) && (

{text.isPartnerAlertHeader}

@@ -542,11 +549,11 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { {text.supportLink} ), - } + }, )}

- )} + )} */}
@@ -561,7 +568,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.firstName, - formState.errors.firstName + formState.errors.firstName, ), } : {})} @@ -579,7 +586,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.lastName, - formState.errors.lastName + formState.errors.lastName, ), } : {})} @@ -611,7 +618,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.country, - formState.errors.countryCode + formState.errors.countryCode, ), } : {})} @@ -710,7 +717,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { validateBillingField(/[a-zA-Z]+/, value) && formatErrorMessage( text.phoneNumber, - text.error.fieldInvalidError + text.error.fieldInvalidError, ); }} disabled={ @@ -721,7 +728,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.phoneNumber, - formState.errors.phoneNumber + formState.errors.phoneNumber, ), } : {})} @@ -740,7 +747,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { !validateBillingField(/^[\x20-\xFF]+$/, value) && formatErrorMessage( text.address, - text.error.invalidCharacterError + text.error.invalidCharacterError, ) } disabled={ @@ -751,7 +758,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.address, - formState.errors.address + formState.errors.address, ), } : {})} @@ -768,7 +775,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { !validateBillingField(/^[\x20-\xFF]+$/, value) && formatErrorMessage( text.city, - text.error.invalidCharacterError + text.error.invalidCharacterError, ) } disabled={ @@ -779,7 +786,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.city, - formState.errors.city + formState.errors.city, ), } : {})} @@ -800,7 +807,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.state, - formState.errors.state + formState.errors.state, ), } : {})} @@ -825,7 +832,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.postalCode, - formState.errors.postalCode + formState.errors.postalCode, ), } : {})} @@ -845,7 +852,7 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { class: classes.ErrorInput, helpText: formatErrorMessage( text.currency, - formState.errors.currency + formState.errors.currency, ), } : {})} @@ -878,7 +885,8 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => { ))} -
+ {/*
+ AL: FLAGGED FOR DELETION { @@ -896,11 +904,11 @@ export const UserInfoFormView = (props: UserInfoFormViewProps) => {

{formatErrorMessage( text.termsAndConditionsLabel, - formState.errors.allowBankingCollection + formState.errors.allowBankingCollection, )}

)} -
+
*/}
(CONNECT_PARTNER); + const [ + completeImpactPartner, + { loading: completeLoading, errors: completeErrors }, + ] = useMutation(COMPLETE_PARTNER); + const { data: tenantData } = useQuery(GET_INDIRECT_TAX_COUNTRY_CODE, {}); const { @@ -149,9 +156,15 @@ export function useUserInfoForm(props: TaxForm) { lastName: user.impactConnection.user.lastName, countryCode: user.impactConnection.publisher.countryCode, currency: user.impactConnection.publisher.currency, + // when creating an impact connection without sending phoneNumber data, the impactAPI defaults the value to "000000" and the phoneNumberCountryCode to "DZ" phoneNumberCountryCode: - user.impactConnection.publisher.phoneNumberCountryCode, - phoneNumber: user.impactConnection.publisher.phoneNumber, + user.impactConnection.publisher.phoneNumber === "0000000" + ? null + : user.impactConnection.publisher.phoneNumberCountryCode, + phoneNumber: + user.impactConnection.publisher.phoneNumber === "0000000" + ? null + : user.impactConnection.publisher.phoneNumber, address: user.impactConnection.publisher.billingAddress, city: user.impactConnection.publisher.billingCity, state: user.impactConnection.publisher.billingState, @@ -249,17 +262,31 @@ export function useUserInfoForm(props: TaxForm) { phoneNumberCountryCode: formData.phoneNumberCountryCode, } as Partial; - const result = await connectImpactPartner({ - vars, - }); + //AL: TODO completePartnerMutation might change + // if user already has an impact connection, call completeImpactPartner to update their information instead of connectPartner + const userData = data?.user; + let result = null; + let connectionResult; + if (userData?.impactConnection?.connectionStatus === "STARTED") { + console.log(vars, "values for completeImpactPartner"); + result = await completeImpactPartner({ + vars, + }); + connectionResult = (result as CompletePartnerResult) + ?.completeImpactConnection; + } else { + result = await connectImpactPartner({ + vars, + }); + connectionResult = (result as ConnectPartnerResult) + ?.createImpactConnection; + } if (!result || (result as Error)?.message) throw new Error(); - if (!(result as ConnectPartnerResult).createImpactConnection?.success) { - // Output backend errors to console for now + if (!connectionResult?.success) { console.error( "Failed to create Impact connection: ", - (result as ConnectPartnerResult).createImpactConnection - .validationErrors, + connectionResult?.validationErrors, ); throw new Error(); @@ -267,8 +294,7 @@ export function useUserInfoForm(props: TaxForm) { await refetch(); - const resultPublisher = (result as ConnectPartnerResult) - .createImpactConnection?.user?.impactConnection?.publisher; + const resultPublisher = connectionResult?.user?.impactConnection?.publisher; const hasValidCurrentDocument = validTaxDocument(resultPublisher?.requiredTaxDocumentType) && @@ -415,9 +441,9 @@ export function useUserInfoForm(props: TaxForm) { step: step?.replace("/", ""), hideState: !hasStates, hideSteps: !!context.hideSteps, - disabled: loading || connectLoading, + disabled: loading || connectLoading || completeLoading, loadingError: !!userError?.message, - loading: loading || connectLoading, + loading: loading || connectLoading || completeLoading, isPartner: !!data?.user?.impactConnection?.publisher, isUser: !!data?.user?.impactConnection?.user,