diff --git a/src/Components/BillingNamePaymentInput.res b/src/Components/BillingNamePaymentInput.res new file mode 100644 index 000000000..c29b23b13 --- /dev/null +++ b/src/Components/BillingNamePaymentInput.res @@ -0,0 +1,59 @@ +open RecoilAtoms +open PaymentType +open Utils + +@react.component +let make = (~paymentType, ~customFieldName=None) => { + let {localeString} = Recoil.useRecoilValueFromAtom(configAtom) + let {fields} = Recoil.useRecoilValueFromAtom(optionAtom) + let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) + + let (billingName, setBillingName) = Recoil.useLoggedRecoilState( + userBillingName, + "billingName", + loggerState, + ) + + let showDetails = getShowDetails(~billingDetails=fields.billingDetails, ~logger=loggerState) + + let changeName = ev => { + let val: string = ReactEvent.Form.target(ev)["value"] + setBillingName(.prev => { + ...prev, + value: val, + errorString: "", + }) + } + let (placeholder, fieldName) = switch customFieldName { + | Some(val) => (val, val) + | None => (localeString.billingNamePlaceholder, localeString.billingNameLabel) + } + let nameRef = React.useRef(Js.Nullable.null) + + let submitCallback = React.useCallback1((ev: Window.event) => { + let json = ev.data->Js.Json.parseExn + let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper + if confirm.doSubmit { + if billingName.value == "" { + setBillingName(.prev => { + ...prev, + errorString: `Please provide your ${fieldName}`, + }) + } + } + }, [billingName]) + submitPaymentData(submitCallback) + + + + +} diff --git a/src/Components/DynamicFields.res b/src/Components/DynamicFields.res new file mode 100644 index 000000000..93948148e --- /dev/null +++ b/src/Components/DynamicFields.res @@ -0,0 +1,450 @@ +open RecoilAtoms +@react.component +let make = (~paymentType, ~list, ~paymentMethod, ~paymentMethodType, ~setRequiredFieldsBody) => { + //<...>// + let paymentMethodTypes = + PaymentMethodsRecord.getPaymentMethodTypeFromList( + ~list, + ~paymentMethod, + ~paymentMethodType, + )->Belt.Option.getWithDefault(PaymentMethodsRecord.defaultPaymentMethodType) + + let requiredFields = if paymentMethod === "card" { + let creditPaymentMethodsRecord = + PaymentMethodsRecord.getPaymentMethodTypeFromList( + ~list, + ~paymentMethod, + ~paymentMethodType="credit", + )->Belt.Option.getWithDefault(PaymentMethodsRecord.defaultPaymentMethodType) + paymentMethodTypes.required_fields + ->Utils.getDictFromJson + ->Js.Dict.entries + ->Js.Array2.concat( + creditPaymentMethodsRecord.required_fields->Utils.getDictFromJson->Js.Dict.entries, + ) + ->Js.Dict.fromArray + ->Js.Json.object_ + } else { + paymentMethodTypes.required_fields + } + + //<...>// + let fieldsArr = + PaymentMethodsRecord.getPaymentMethodFields(paymentMethodType, requiredFields) + ->Utils.removeDuplicate + ->Js.Array2.filter(item => item !== None) + //<...>// + + let {config, themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom) + + let logger = Recoil.useRecoilValueFromAtom(loggerAtom) + + let setAreRequiredFieldsValid = Recoil.useSetRecoilState(areRequiredFieldsValid) + let setAreRequiredFieldsEmpty = Recoil.useSetRecoilState(areRequiredFieldsEmpty) + + let (email, setEmail) = Recoil.useLoggedRecoilState(userEmailAddress, "email", logger) + let (line1, setLine1) = Recoil.useLoggedRecoilState(userAddressline1, "line1", logger) + let (line2, setLine2) = Recoil.useLoggedRecoilState(userAddressline1, "line2", logger) + let (city, setCity) = Recoil.useLoggedRecoilState(userAddressline1, "city", logger) + let (state, setState) = Recoil.useLoggedRecoilState(userAddressState, "state", logger) + let (postalCode, setPostalCode) = Recoil.useLoggedRecoilState( + userAddressPincode, + "postal_code", + logger, + ) + let (postalCodes, setPostalCodes) = React.useState(_ => [PostalCodeType.defaultPostalCode]) + let (fullName, setFullName) = Recoil.useLoggedRecoilState(userFullName, "fullName", logger) + let (blikCode, setBlikCode) = Recoil.useLoggedRecoilState(userBlikCode, "blikCode", logger) + let (phone, _) = Recoil.useLoggedRecoilState(userPhoneNumber, "phone", logger) + let (currency, setCurrency) = Recoil.useLoggedRecoilState(userCurrency, "currency", logger) + let (billingName, setBillingName) = Recoil.useLoggedRecoilState( + userBillingName, + "billingName", + logger, + ) + let line1Ref = React.useRef(Js.Nullable.null) + let line2Ref = React.useRef(Js.Nullable.null) + let cityRef = React.useRef(Js.Nullable.null) + let postalRef = React.useRef(Js.Nullable.null) + let (selectedBank, setSelectedBank) = Recoil.useRecoilState(userBank) + let (country, setCountry) = Recoil.useRecoilState(userCountry) + + let (stateJson, setStatesJson) = React.useState(_ => None) + + let bankNames = + Bank.getBanks(paymentMethodType)->Utils.getBankNames(paymentMethodTypes.bank_names) + let countryNames = Utils.getCountryNames(Country.getCountry(paymentMethodType)) + + let setCurrency = val => { + setCurrency(. val) + } + let setSelectedBank = val => { + setSelectedBank(. val) + } + let setCountry = val => { + setCountry(. val) + } + + React.useEffect0(() => { + let clientTimeZone = Utils.dateTimeFormat(.).resolvedOptions(.).timeZone + let clientCountry = Utils.getClientCountry(clientTimeZone) + setCountry(_ => clientCountry.countryName) + let bank = bankNames->Belt.Array.get(0)->Belt.Option.getWithDefault("") + setSelectedBank(_ => bank) + None + }) + + React.useEffect0(() => { + open Promise + // Dynamically import/download Postal codes and states JSON + PostalCodeType.importPostalCode("./../PostalCodes.bs.js") + ->then(res => { + setPostalCodes(_ => res.default) + resolve() + }) + ->catch(_ => { + setPostalCodes(_ => [PostalCodeType.defaultPostalCode]) + resolve() + }) + ->ignore + AddressPaymentInput.importStates("./../States.json") + ->then(res => { + setStatesJson(_ => Some(res.states)) + resolve() + }) + ->catch(_ => { + setStatesJson(_ => None) + resolve() + }) + ->ignore + + None + }) + + let regex = CardUtils.postalRegex( + postalCodes, + ~country={Utils.getCountryCode(country).isoAlpha2}, + (), + ) + + let onPostalChange = ev => { + let val = ReactEvent.Form.target(ev)["value"] + + setPostalCode(.prev => { + ...prev, + value: val, + errorString: "", + }) + if regex !== "" && Js.Re.test_(regex->Js.Re.fromString, val) { + CardUtils.blurRef(postalRef) + } + } + + let onPostalBlur = ev => { + let val = ReactEvent.Focus.target(ev)["value"] + if regex !== "" && Js.Re.test_(regex->Js.Re.fromString, val) && val !== "" { + setPostalCode(.prev => { + ...prev, + isValid: Some(true), + errorString: "", + }) + } else if regex !== "" && !Js.Re.test_(regex->Js.Re.fromString, val) && val !== "" { + setPostalCode(.prev => { + ...prev, + isValid: Some(false), + errorString: "Invalid postal code", + }) + } + } + + React.useEffect7(() => { + let areRequiredFieldsValid = fieldsArr->Js.Array2.reduce((acc, paymentMethodFields) => { + acc && + switch paymentMethodFields { + | Email => email.isValid + | FullName => Some(fullName.value !== "") + | Country => Some(country !== "") + | BillingName => Some(billingName.value !== "") + | AddressLine1 => Some(line1.value !== "") + | AddressLine2 => Some(line2.value !== "") + | _ => Some(true) + }->Belt.Option.getWithDefault(false) + }, true) + setAreRequiredFieldsValid(._ => areRequiredFieldsValid) + + let areRequiredFieldsEmpty = fieldsArr->Js.Array2.reduce((acc, paymentMethodFields) => { + acc || + switch paymentMethodFields { + | Email => email.value === "" + | FullName => fullName.value === "" + | Country => country === "" + | BillingName => billingName.value === "" + | AddressLine1 => line1.value === "" + | AddressLine2 => line2.value === "" + | _ => false + } + }, false) + setAreRequiredFieldsEmpty(._ => areRequiredFieldsEmpty) + None + }, (fieldsArr, email, fullName, country, billingName, line1, line2)) + + let requiredFieldsType = + requiredFields + ->Utils.getDictFromJson + ->Js.Dict.values + ->Js.Array2.map(item => + item->Utils.getDictFromJson->PaymentMethodsRecord.getRequiredFieldsFromJson + ) + + React.useEffect0(() => { + let setFields = ( + setMethod: (. RecoilAtomTypes.field => RecoilAtomTypes.field) => unit, + field: RecoilAtomTypes.field, + fieldValue, + ) => { + field.value === "" + ? setMethod(.prev => { + ...prev, + value: fieldValue, + }) + : () + } + + requiredFieldsType->Js.Array2.forEach(requiredField => { + let value = requiredField.value + switch requiredField.field_type { + | Email => { + setFields(setEmail, email, value) + let newEmail: RecoilAtomTypes.field = { + value: value, + isValid: None, + errorString: "", + } + Utils.checkEmailValid(newEmail, setEmail) + } + | FullName => setFields(setFullName, fullName, value) + | AddressLine1 => setFields(setLine1, line1, value) + | AddressLine2 => setFields(setLine2, line2, value) + | BlikCode => setFields(setBlikCode, blikCode, value) + | BillingName => setFields(setBillingName, billingName, value) + | AddressCountry(_) => { + let countryCode = + Country.getCountry(paymentMethodType) + ->Js.Array2.filter(item => item.isoAlpha2 === value) + ->Belt.Array.get(0) + ->Belt.Option.getWithDefault(Country.defaultTimeZone) + setCountry(_ => countryCode.countryName) + } + | _ => () + } + }) + None + }) + + React.useEffect1(() => { + let getName = (item: PaymentMethodsRecord.required_fields, field: RecoilAtomTypes.field) => { + let fieldNameArr = field.value->Js.String2.split(" ") + let requiredFieldsArr = item.required_field->Js.String2.split(".") + switch requiredFieldsArr + ->Belt.Array.get(requiredFieldsArr->Belt.Array.length - 1) + ->Belt.Option.getWithDefault("") { + | "first_name" => fieldNameArr->Belt.Array.get(0)->Belt.Option.getWithDefault(field.value) + | "last_name" => fieldNameArr->Belt.Array.sliceToEnd(1)->Js.Array2.reduce((acc, item) => { + acc ++ item + }, "") + | _ => field.value + } + } + + let requiredFieldsBody = + requiredFieldsType + ->Js.Array2.filter(item => item.field_type !== None) + ->Js.Array2.reduce((acc, item) => { + let value = switch item.field_type { + | Email => email.value + | FullName => getName(item, fullName) + | AddressLine1 => line1.value + | BlikCode => blikCode.value + | PhoneNumber => phone.value + | Currency(_) => currency + | Country => country + | Bank => selectedBank + | BillingName => getName(item, billingName) + | AddressCountry(_) => { + let countryCode = + Country.getCountry(paymentMethodType) + ->Js.Array2.filter(item => item.countryName === country) + ->Belt.Array.get(0) + ->Belt.Option.getWithDefault(Country.defaultTimeZone) + countryCode.isoAlpha2 + } + | _ => "" + } + acc->Js.Dict.set(item.required_field, value->Js.Json.string) + acc + }, Js.Dict.empty()) + + setRequiredFieldsBody(_ => requiredFieldsBody) + None + }, [ + fullName.value, + email.value, + line1.value, + blikCode.value, + phone.value, + currency, + billingName.value, + country, + ]) + + let bottomElement = + +
+ {fieldsArr + ->Js.Array2.mapi((item, index) => { +
Js.Int.toString} + className="flex flex-col w-full place-content-between" + style={ReactDOMStyle.make( + ~marginTop=index !== 0 || paymentMethod === "card" ? themeObj.spacingGridColumn : "", + ~gridColumnGap=themeObj.spacingGridRow, + (), + )}> + {switch item { + | FullName => + | BillingName => + | Email => + | PhoneNumber => + | AddressLine1 => + { + setLine1(.prev => { + ...prev, + value: ReactEvent.Form.target(ev)["value"], + }) + }} + paymentType + type_="text" + name="line1" + inputRef=line1Ref + placeholder=localeString.line1Placeholder + /> + | AddressLine2 => + { + setLine2(.prev => { + ...prev, + value: ReactEvent.Form.target(ev)["value"], + }) + }} + paymentType + type_="text" + name="line2" + inputRef=line2Ref + placeholder=localeString.line2Placeholder + /> + | AddressCity => + { + setCity(.prev => { + ...prev, + value: ReactEvent.Form.target(ev)["value"], + }) + }} + paymentType + type_="text" + name="city" + inputRef=cityRef + placeholder=localeString.cityLabel + /> + | AddressState => + switch stateJson { + | Some(options) => + Utils.getStateNames({ + value: country, + isValid: None, + errorString: "", + })} + defaultSelected=false + /> + | None => React.null + } + | AddressPincode => + + | BlikCode => + | Currency(currencyArr) => + + | Country => + + | AddressCountry(countryArr) => + + | Bank => + + | SpecialField(element) => element + | InfoElement => <> + + {if fieldsArr->Js.Array2.length > 1 { + bottomElement + } else { + + }} + + | None => React.null + }} +
+ }) + ->React.array} +
+} diff --git a/src/LocaleString.res b/src/LocaleString.res index c36399c2f..34c133a25 100644 --- a/src/LocaleString.res +++ b/src/LocaleString.res @@ -46,6 +46,8 @@ type localeStrings = { surchargeMsgPercentage: string => string, surchargeMsgAmountForCard: string => string, surchargeMsgPercentageForCard: string => string, + billingNameLabel: string, + billingNamePlaceholder: string, } let defaultLocale = { @@ -101,6 +103,8 @@ let defaultLocale = { `A surcharge amount of upto ${str} will be applied for this transaction`, surchargeMsgPercentageForCard: str => `A surcharge of upto ${str}% will be applied for this transaction`, + billingNameLabel: "Billing name", + billingNamePlaceholder: "First and last name", } type locale = {localeStrings: array} @@ -158,6 +162,8 @@ let localeStrings = [ `A surcharge amount of upto ${str} will be applied for this transaction`, surchargeMsgPercentageForCard: str => `A surcharge of upto ${str}% will be applied for this transaction`, + billingNameLabel: "Billing name", + billingNamePlaceholder: "First and last name", }, { locale: "he", @@ -210,7 +216,10 @@ let localeStrings = [ surchargeMsgPercentage: str => `תוספת של ${str}% תחול עבור עסקה זו`, surchargeMsgAmountForCard: str => `סכום היטל של עד ${str} יחול עבור עסקה זו`, - surchargeMsgPercentageForCard: str => `תוספת של עד ${str}% תחול על עסקה זו`, + surchargeMsgPercentageForCard: str => + `תוספת של עד ${str}% תחול על עסקה זו`, + billingNameLabel: `שם החיוב`, + billingNamePlaceholder: `שם פרטי ושם משפחה`, }, { locale: `fr`, @@ -261,11 +270,14 @@ let localeStrings = [ card: `Carte`, surchargeMsgAmount: str => `Un montant supplémentaire d'${str} sera appliqué pour cette transaction`, - surchargeMsgPercentage: str => `Un supplément de ${str}% sera appliqué pour cette transaction`, + surchargeMsgPercentage: str => + `Un supplément de ${str}% sera appliqué pour cette transaction`, surchargeMsgAmountForCard: str => `Un montant supplémentaire allant jusqu'à ${str} sera appliqué pour cette transaction.`, surchargeMsgPercentageForCard: str => `Un supplément allant jusqu'à ${str}% sera appliqué pour cette transaction`, + billingNameLabel: `Nom de facturation`, + billingNamePlaceholder: `Prénom et nom de famille`, }, { locale: "en-GB", @@ -320,6 +332,8 @@ let localeStrings = [ `A surcharge amount of upto ${str} will be applied for this transaction`, surchargeMsgPercentageForCard: str => `A surcharge of upto ${str}% will be applied for this transaction`, + billingNameLabel: "Billing name", + billingNamePlaceholder: "First and last name", }, { locale: "ar", @@ -376,6 +390,8 @@ let localeStrings = [ `سيتم تطبيق مبلغ إضافي يصل إلى ${str} على هذه المعاملة`, surchargeMsgPercentageForCard: str => `سيتم تطبيق رسوم إضافية تصل إلى ${str}% على هذه المعاملة`, + billingNameLabel: `اسم الفواتير`, + billingNamePlaceholder: `الاسم الأول والاسم الأخير`, }, { locale: "ja", @@ -425,11 +441,14 @@ let localeStrings = [ enterValidDetailsText: `有効な詳細を入力してください`, card: `カード`, surchargeMsgAmount: str => `この取引には ${str} の追加料金が適用されます`, - surchargeMsgPercentage: str => `この取引には ${str}% の追加料金が適用されます`, + surchargeMsgPercentage: str => + `この取引には ${str}% の追加料金が適用されます`, surchargeMsgAmountForCard: str => `この取引には ${str} までの追加料金が適用されます`, surchargeMsgPercentageForCard: str => `この取引には ${str}% までの追加料金が適用されます`, + billingNameLabel: `課金名`, + billingNamePlaceholder: `名前と苗字`, }, { locale: "de", @@ -485,5 +504,7 @@ let localeStrings = [ `Für diese Transaktion wird ein Zuschlagsbetrag von bis zu ${str} erhoben`, surchargeMsgPercentageForCard: str => `Für diese Transaktion wird ein Zuschlag von bis zu ${str}% erhoben`, + billingNameLabel: `Abrechnungsname`, + billingNamePlaceholder: `Vor-und Nachname`, }, ] diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res index af3b9650c..0368bf04d 100644 --- a/src/Payments/CardPayment.res +++ b/src/Payments/CardPayment.res @@ -77,6 +77,10 @@ let make = ( postFailedSubmitResponse(~errortype="validation_error", ~message) } + let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Js.Dict.empty()) + + let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid) + React.useEffect1(() => { switch customerPaymentMethods { | LoadingSavedCards => () @@ -195,17 +199,30 @@ let make = ( let validFormat = isCardValid->Belt.Option.getWithDefault(false) && isExpiryValid->Belt.Option.getWithDefault(false) && - (isBancontact || isCVCValid->Belt.Option.getWithDefault(false)) + (isBancontact || isCVCValid->Belt.Option.getWithDefault(false)) && + areRequiredFieldsValid if validFormat && (showFields || isBancontact) { intent( - ~bodyArr={isBancontact ? banContactBody : cardBody}, + ~bodyArr={ + (isBancontact ? banContactBody : cardBody) + ->Js.Dict.fromArray + ->Js.Json.object_ + ->OrcaUtils.flattenObject(true) + ->OrcaUtils.mergeTwoFlattenedJsonDicts(requiredFieldsBody) + ->OrcaUtils.getArrayOfTupleFromDict + }, ~confirmParam=confirm.confirmParams, ~handleUserError=false, (), ) } else if complete && !empty { intent( - ~bodyArr=savedCardBody, + ~bodyArr=savedCardBody + ->Js.Dict.fromArray + ->Js.Json.object_ + ->OrcaUtils.flattenObject(true) + ->OrcaUtils.mergeTwoFlattenedJsonDicts(requiredFieldsBody) + ->OrcaUtils.getArrayOfTupleFromDict, ~confirmParam=confirm.confirmParams, ~handleUserError=false, (), @@ -311,6 +328,11 @@ let make = ( + Js.Array.length !== 0}> + +
diff --git a/src/Payments/PaymentMethodsRecord.res b/src/Payments/PaymentMethodsRecord.res index cd554af48..573ada620 100644 --- a/src/Payments/PaymentMethodsRecord.res +++ b/src/Payments/PaymentMethodsRecord.res @@ -16,7 +16,7 @@ type paymentMethodsFields = | AddressCity | AddressPincode | AddressState - | AddressCountry + | AddressCountry(array) | BlikCode | Currency(array) type bankNames = { @@ -233,7 +233,7 @@ let paymentMethodsFields = [ paymentMethodName: "blik", icon: Some(icon("blik", ~size=19, ~width=25)), displayName: "Blik", - fields: [SpecialField(), InfoElement], + fields: [InfoElement], miniIcon: None, }, { @@ -439,29 +439,28 @@ let getPaymentMethodsFieldTypeFromString = str => { | "user_address_city" => AddressCity | "user_address_pincode" => AddressPincode | "user_address_state" => AddressState - | "user_address_country" => AddressCountry | "user_blik_code" => BlikCode + | "user_billing_name" => BillingName | _ => None } } let getPaymentMethodsFieldTypeFromDict = dict => { let keysArr = dict->Js.Dict.keys - let key = - keysArr->Js.Array2.find(item => item === "user_currency")->Belt.Option.getWithDefault("") + let key = keysArr->Belt.Array.get(0)->Belt.Option.getWithDefault("") switch key { | "user_currency" => { - let options = - dict - ->Js.Dict.get("user_currency") - ->Belt.Option.flatMap(Js.Json.decodeObject) - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->Js.Dict.get("options") - ->Belt.Option.flatMap(Js.Json.decodeArray) - ->Belt.Option.getWithDefault([]) - ->Belt.Array.keepMap(Js.Json.decodeString) + let options = dict->Utils.getArrayValFromJsonDict("user_currency", "options") Currency(options) } + | "user_address_country" => { + let options = dict->Utils.getArrayValFromJsonDict("user_address_country", "options") + switch options->Belt.Array.get(0)->Belt.Option.getWithDefault("") { + | "" => None + | "ALL" => AddressCountry(Country.country->Js.Array2.map(item => item.countryName)) + | _ => AddressCountry(options) + } + } | _ => None } } @@ -494,9 +493,11 @@ let getRequiredFieldsFromJson = dict => { } } +let dynamicFieldsEnabledPaymentMethods = ["crypto_currency", "debit", "credit", "blik"] + let getPaymentMethodFields = (paymentMethod, requiredFields) => { let requiredFieldsArr = - paymentMethod === "crypto_currency" + dynamicFieldsEnabledPaymentMethods->Js.Array2.includes(paymentMethod) ? requiredFields ->Utils.getDictFromJson ->Js.Dict.values diff --git a/src/Payments/PaymentMethodsWrapper.res b/src/Payments/PaymentMethodsWrapper.res index 45ef689ec..0bb04d97e 100644 --- a/src/Payments/PaymentMethodsWrapper.res +++ b/src/Payments/PaymentMethodsWrapper.res @@ -9,11 +9,11 @@ type props = { } let default = (props: props) => { - let {publishableKey, iframeId} = Recoil.useRecoilValueFromAtom(keys) + let {iframeId} = Recoil.useRecoilValueFromAtom(keys) let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) let blikCode = Recoil.useRecoilValueFromAtom(userBlikCode) let phoneNumber = Recoil.useRecoilValueFromAtom(userPhoneNumber) - let {config, themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom) + let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom) let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Other) let optionPaymentMethodDetails = props.list @@ -32,84 +32,25 @@ let default = (props: props) => { ->Belt.Option.getWithDefault(RedirectToURL) let (fullName, _) = Recoil.useLoggedRecoilState(userFullName, "fullName", loggerState) let (email, _) = Recoil.useLoggedRecoilState(userEmailAddress, "email", loggerState) - let (currency, setCurrency) = Recoil.useLoggedRecoilState(userCurrency, "currency", loggerState) - let (country, setCountry) = Recoil.useRecoilState(userCountry) - let (selectedBank, setSelectedBank) = Recoil.useRecoilState(userBank) - let {fields} = Recoil.useRecoilValueFromAtom(optionAtom) + let (currency, _) = Recoil.useLoggedRecoilState(userCurrency, "currency", loggerState) + let (country, _) = Recoil.useRecoilState(userCountry) + let (selectedBank, _) = Recoil.useRecoilState(userBank) let setFieldComplete = Recoil.useSetRecoilState(fieldsComplete) - let showDetails = PaymentType.getShowAddressDetails( - ~billingDetails=fields.billingDetails, - ~logger=loggerState, - ) - let countryNames = Utils.getCountryNames(Country.getCountry(props.paymentMethodName)) - let bankNames = - Bank.getBanks(props.paymentMethodName)->Utils.getBankNames(paymentMethodDetails.bankNames) - let setCountry = val => { - setCountry(. val) - } - let setCurrency = val => { - setCurrency(. val) - } - let setSelectedBank = val => { - setSelectedBank(. val) - } let cleanBlik = str => str->Js.String2.replaceByRe(%re("/-/g"), "") let cleanPhoneNumber = str => str->Js.String2.replaceByRe(%re("/\s/g"), "") - React.useEffect0(() => { - let clientTimeZone = dateTimeFormat(.).resolvedOptions(.).timeZone - let clientCountry = getClientCountry(clientTimeZone) - setCountry(_ => clientCountry.countryName) - let bank = bankNames->Belt.Array.get(0)->Belt.Option.getWithDefault("") - setSelectedBank(_ => bank) - None - }) - //<...>// - let requiredField = - PaymentMethodsRecord.getPaymentMethodTypeFromList( - ~list=props.list, - ~paymentMethod=paymentMethodDetails.methodType, - ~paymentMethodType=paymentMethodDetails.paymentMethodName, - )->Belt.Option.getWithDefault(PaymentMethodsRecord.defaultPaymentMethodType) - let fieldsArr = - props.paymentMethodName->PaymentMethodsRecord.getPaymentMethodFields( - requiredField.required_fields, - ) - //<...>// - let complete = React.useMemo4(() => { - fieldsArr - ->Js.Array2.map(field => { - switch field { - | Email => email.value != "" && email.isValid->Belt.Option.getWithDefault(false) - | FullName => fullName.value !== "" - | Country => country !== "" - | _ => true - } - }) - ->Js.Array2.reduce((acc, condition) => { - acc && condition - }, true) - }, (props.paymentMethodName, email, fullName, country)) + + let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Js.Dict.empty()) + let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid) + let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty) + + let complete = areRequiredFieldsValid React.useEffect1(() => { setFieldComplete(._ => complete) None }, [complete]) - let empty = React.useMemo4(() => { - props.paymentMethodName - ->PaymentMethodsRecord.getPaymentMethodFields(requiredField.required_fields) - ->Js.Array2.map(field => { - switch field { - | Email => email.value == "" - | FullName => fullName.value == "" - | Country => country == "" - | _ => false - } - }) - ->Js.Array2.reduce((acc, condition) => { - acc || condition - }, false) - }, (props.paymentMethodName, email, fullName, country)) + let empty = areRequiredFieldsEmpty React.useEffect2(() => { handlePostMessageEvents( @@ -148,7 +89,12 @@ let default = (props: props) => { ~phoneNumber=phoneNumber.value->cleanPhoneNumber, ~paymentExperience=paymentFlow, ~currency, - ), + ) + ->Js.Dict.fromArray + ->Js.Json.object_ + ->OrcaUtils.flattenObject(true) + ->OrcaUtils.mergeTwoFlattenedJsonDicts(requiredFieldsBody) + ->OrcaUtils.getArrayOfTupleFromDict, ~confirmParam=confirm.confirmParams, ~handleUserError=false, ~iframeId, @@ -165,63 +111,20 @@ let default = (props: props) => { blikCode, props.paymentMethodName, phoneNumber.value, - (selectedBank, currency), + (selectedBank, currency, requiredFieldsBody), )) submitPaymentData(submitCallback) - let bottomElement =
- {fieldsArr - ->Js.Array2.map(field => { - switch field { - | Email => - | FullName => - | Country => - - - - | Bank => - - | SpecialField(element) => element - | InfoElement => <> - - {if fieldsArr->Js.Array2.length > 1 { - - } else { - - }} - - | Currency(currencyArr) => - - | _ => React.null - } - }) - ->React.array} + Js.Array.length !== 0}> + +
} diff --git a/src/Utilities/RecoilAtoms.res b/src/Utilities/RecoilAtoms.res index f112f325b..02166074f 100644 --- a/src/Utilities/RecoilAtoms.res +++ b/src/Utilities/RecoilAtoms.res @@ -49,3 +49,6 @@ let fieldsComplete = Recoil.atom(. "fieldsComplete", false) let isManualRetryEnabled = Recoil.atom(. "isManualRetryEnabled", false) let userCurrency = Recoil.atom(. "userCurrency", "") let isShowOrPayUsing = Recoil.atom(. "isShowOrPayUsing", false) +let areRequiredFieldsValid = Recoil.atom(. "areRequiredFieldsValid", false) +let areRequiredFieldsEmpty = Recoil.atom(. "areRequiredFieldsEmpty", true) +let userBillingName = Recoil.atom(. "userBillingName", defaultFieldValues) diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index 8271a52bc..cb7e57d1a 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -347,7 +347,7 @@ let getClientCountry = clientTimeZone => { ->Belt.Option.getWithDefault(Country.defaultTimeZone) } -let removeDuplicate = (arr: array) => { +let removeDuplicate = arr => { arr->Js.Array2.filteri((item, i) => { arr->Js.Array2.indexOf(item) === i }) diff --git a/src/orca-loader/OrcaUtils.res b/src/orca-loader/OrcaUtils.res index 0623fd87b..aac452edf 100644 --- a/src/orca-loader/OrcaUtils.res +++ b/src/orca-loader/OrcaUtils.res @@ -315,3 +315,18 @@ let getThemePromise = dict => { | _ => None } } + +let mergeTwoFlattenedJsonDicts = (dict1, dict2) => { + dict1 + ->Js.Dict.entries + ->Js.Array2.concat(dict2->Js.Dict.entries) + ->Js.Dict.fromArray + ->Js.Json.object_ + ->unflattenObject +} + +let getArrayOfTupleFromDict = dict => { + dict + ->Js.Dict.keys + ->Belt.Array.map(key => (key, Js.Dict.get(dict, key)->Belt.Option.getWithDefault(Js.Json.null))) +}