{"version":3,"file":"static/js/main.f49d0d47.chunk.js","sources":["pages/auth/NewPasswordInput.module.scss","pages/apply/PlaidSecurityDepositTransfer.module.scss","pages/transfersources/AddManual.module.scss","pages/apply/SecondaryInspection.module.scss","pages/apply/UploadDocument.module.scss","pages/apply/AdditionalVerificationDocuments.module.scss","components/Skeleton/MiniAppSkeleton.module.scss","components/CardImage/index.module.scss","pages/transfersources/InboundTransferSource.module.scss","pages/apply/AddAddress.module.scss","pages/apply/ChoosePlan.module.scss","components/MiniAppDynamicComponent/MiniAppDynamicComponent.module.scss","components/ShareModal/ShareModal.module.scss","pages/home/home.module.scss","pages/cards/ActivateCard.module.scss","pages/transfersources/AddBank.module.scss","pages/transfersources/VerifyMicroDeposits.module.scss","pages/payment/ConfirmPayment.module.scss","pages/apply/AddPhone.module.scss","pages/apply/DownloadApp.module.scss","pages/home/PopupView.module.scss","pages/miniapps/signupbar.module.scss","pages/account/Statements.module.scss","pages/InfoBanner.module.scss","pages/apply/PullingCredit.module.scss","pages/apply/Verifying.module.scss","pages/NavBody.module.scss","pages/account/ReferAFriend.module.scss","pages/account/ChangePassword.module.scss","pages/transfersources/AddInstant.module.scss","ChromeExtension.module.scss","App.module.scss","pages/auth/index.module.scss","pages/transactions/TransactionFilter.module.scss","pages/apply/PreApproved.module.scss","components/LoadingAnimation/LoadingAnimation.module.scss","pages/apply/StateNotAvailable.module.scss","pages/apply/index.module.scss","components/PlaidButton/plaidbutton.module.scss","pages/miniapps/MiniAppFullScreen.module.scss","pages/cards/AddCard.module.scss","pages/miniapps/MiniappDetails.module.scss","components/Logo/index.tsx","pages/auth/Login.tsx","pages/auth/NewPasswordInput.tsx","pages/auth/ForgotPassword.tsx","services/analytics.ts","pages/auth/Prospect.tsx","pages/auth/index.tsx","config.ts","components/KTextInput/KPlacesAutoCompleteInput.tsx","services/klutchHooks.ts","pages/apply/AddAddress.tsx","pages/apply/AddAnnualIncome.tsx","pages/apply/AddGoals.tsx","pages/apply/AddPhone.tsx","pages/apply/AddSSN.tsx","pages/apply/CreditFrozen.tsx","pages/apply/DownloadApp.tsx","helper.ts","hooks/useRevolvingLoan.ts","pages/apply/PreApproved.tsx","pages/apply/PullingCredit.tsx","pages/apply/Reject.tsx","pages/apply/vouched-script.js","components/LoadingAnimation/LoadingAnimation.tsx","pages/apply/SecondaryInspection.tsx","pages/apply/StateNotAvaliable.tsx","pages/apply/ThankYou.tsx","hooks/useQuery.ts","pages/apply/VerifyPhone.tsx","pages/apply/Veryfing.tsx","pages/apply/UploadDocument.tsx","pages/apply/ChoosePlan.tsx","components/PlaidButton/plaidbutton.tsx","pages/apply/SecurityDeposit.tsx","pages/apply/PlaidSecurityDepositTransfer.tsx","pages/apply/AdditionalVerificationDocuments.tsx","pages/apply/index.tsx","pages/NavBody.tsx","hooks/useRecipes.ts","components/MiniAppDynamicComponent/MiniAppDynamicComponent.tsx","components/Skeleton/MiniAppSkeleton.tsx","pages/home/PopupView.tsx","pages/home/Home.tsx","hooks/useCards.ts","hooks/useTransactions.ts","pages/transactions/TransactionFilterView.tsx","pages/transactions/CategoryChooserView.tsx","pages/transactions/TransactionsList.tsx","pages/transactions/TransactionsDetails.tsx","pages/transactions/index.tsx","pages/cards/CardLists.tsx","components/CardImage/index.tsx","components/Ecommerce/PaymentsModal.tsx","pages/cards/AddCard.tsx","pages/cards/ActivateCard.tsx","pages/cards/index.tsx","pages/miniapps/signupbar.tsx","pages/miniapps/MiniApps.tsx","components/ShareModal/ShareModal.tsx","pages/miniapps/MiniAppDetails.tsx","pages/miniapps/MiniAppFullScreen.tsx","pages/miniapps/index.tsx","hooks/useUser.ts","pages/account/AccountPage.tsx","pages/account/ReferAFriend.tsx","pages/account/SecurityAndLegal.tsx","pages/account/CreditTerms.tsx","pages/account/Logout.tsx","pages/account/UpdateDetails.tsx","pages/account/ChangePassword.tsx","pages/account/Statements.tsx","pages/account/index.tsx","hooks/useTransfersSources.ts","pages/transfersources/InboundTransferSource.tsx","pages/transfersources/BankAccounts.tsx","pages/transfersources/AddBank.tsx","pages/transfersources/AddInstant.tsx","pages/transfersources/AddManual.tsx","pages/transfersources/VerifyMicroDeposits.tsx","pages/transfersources/index.tsx","pages/payment/SelectPayment.tsx","pages/payment/ConfirmPayment.tsx","pages/payment/index.tsx","pages/notfound/NotFound.tsx","pages/InfoBanner.tsx","pages/NavRouter.tsx","pages/TokenSwap.tsx","App.tsx","ChromeExtension.tsx","index.tsx","pages/transactions/TransactionsList.module.scss","pages/miniapps/MiniApps.module.scss","pages/auth/Signup.module.scss","pages/apply/AddSSN.module.scss","pages/account/UpdateDetails.module.scss","pages/transactions/CategoryChooserView.module.scss","pages/NavRouter.module.scss","pages/transactions/TransactionsDetails.module.scss","pages/apply/AddGoals.module.scss","components/Ecommerce/PaymentsModal.module.scss","pages/account/AccountPage.module.scss","pages/payment/SelectPayment.module.scss","pages/apply/VerifyPhone.module.scss","pages/apply/SecurityDeposit.module.scss","pages/transfersources/BankAccounts.module.scss","pages/cards/CardLists.module.scss","pages/account/SecurityAndLegal.module.scss"],"sourceRoot":"","sourcesContent":["// extracted by mini-css-extract-plugin\nmodule.exports = {\"passwordReqs\":\"NewPasswordInput_passwordReqs__3rfxO\",\"passwordreqsRed\":\"NewPasswordInput_passwordreqsRed__36JsC\",\"passwordreqsGreen\":\"NewPasswordInput_passwordreqsGreen__51Qvy\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"PlaidSecurityDepositTransfer_container__tA0Xm\",\"info\":\"PlaidSecurityDepositTransfer_info__FALwU\",\"form\":\"PlaidSecurityDepositTransfer_form__O85T4\",\"transferSource\":\"PlaidSecurityDepositTransfer_transferSource__221__\",\"smallPrint\":\"PlaidSecurityDepositTransfer_smallPrint__m7Tp_\",\"ineligble\":\"PlaidSecurityDepositTransfer_ineligble__3iz_1\",\"paymentSent\":\"PlaidSecurityDepositTransfer_paymentSent__oqAz9\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"AddManual_main__1EGjR\",\"button\":\"AddManual_button__3R2G_\",\"select\":\"AddManual_select__2Z3ZD\",\"option\":\"AddManual_option__2u_pQ\",\"added\":\"AddManual_added__2lrJZ\",\"checkmark\":\"AddManual_checkmark__27ozT\",\"error\":\"AddManual_error__3KPMo\",\"skeleton-loading\":\"AddManual_skeleton-loading__2uNfw\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"SecondaryInspection_container__3yZed\",\"vouched\":\"SecondaryInspection_vouched__UVCqB\",\"vouchedInitialized\":\"SecondaryInspection_vouchedInitialized__3oYIQ\",\"failed\":\"SecondaryInspection_failed__2hOXq\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"UploadDocument_container__3TNJc\",\"dropzone\":\"UploadDocument_dropzone__2yvdU\",\"serverError\":\"UploadDocument_serverError__3Mtgc\",\"fileName\":\"UploadDocument_fileName__iSQJa\",\"thankyouparagraphs\":\"UploadDocument_thankyouparagraphs__2ryaY\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"AdditionalVerificationDocuments_container__25ysL\",\"fieldList\":\"AdditionalVerificationDocuments_fieldList__2BM8I\",\"documentsModalBody\":\"AdditionalVerificationDocuments_documentsModalBody__BsTOS\",\"select\":\"AdditionalVerificationDocuments_select__3Wsld\",\"selectOption\":\"AdditionalVerificationDocuments_selectOption__1MiQV\",\"dropzone\":\"AdditionalVerificationDocuments_dropzone__3Y_hR\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"panel\":\"MiniAppSkeleton_panel__1QthN\",\"title\":\"MiniAppSkeleton_title__LJHj5\",\"circle\":\"MiniAppSkeleton_circle__200z6\",\"line\":\"MiniAppSkeleton_line__X0wra\",\"skeleton-loading\":\"MiniAppSkeleton_skeleton-loading__31ag-\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"cardImage\":\"CardImage_cardImage__3N3GM\",\"logo\":\"CardImage_logo__j8lJK\",\"cardName\":\"CardImage_cardName__1xYuf\",\"cardNumber\":\"CardImage_cardNumber__1AfyN\",\"expirationDate\":\"CardImage_expirationDate__37XmD\",\"cvv\":\"CardImage_cvv__8Gwhf\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"content\":\"InboundTransferSource_content__Rj8uv\",\"infoPair\":\"InboundTransferSource_infoPair__3r2hp\",\"skeleton-loading\":\"InboundTransferSource_skeleton-loading__1ooth\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"AddAddress_container__rbDqT\",\"serverError\":\"AddAddress_serverError__ZrKeC\",\"address\":\"AddAddress_address__3mEi1\",\"buttonBar\":\"AddAddress_buttonBar__tYglz\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"ChoosePlan_container__1mWXZ\",\"serverError\":\"ChoosePlan_serverError__3dB-x\",\"plan\":\"ChoosePlan_plan__34M7x\",\"basic\":\"ChoosePlan_basic__2Ixki\",\"unavailable\":\"ChoosePlan_unavailable__9ZJzq\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"panel\":\"MiniAppDynamicComponent_panel__2J_Ik\",\"SMALL\":\"MiniAppDynamicComponent_SMALL__ACqyF\",\"MEDIUM\":\"MiniAppDynamicComponent_MEDIUM__1T9zx\",\"LARGE\":\"MiniAppDynamicComponent_LARGE__1If-Y\",\"FULLSCREEN\":\"MiniAppDynamicComponent_FULLSCREEN__2x0Nd\",\"body\":\"MiniAppDynamicComponent_body__nFq7f\",\"sorry\":\"MiniAppDynamicComponent_sorry__28grL\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"shareMain\":\"ShareModal_shareMain__OvwCm\",\"shareTitle\":\"ShareModal_shareTitle__1ty6i\",\"shareIcons\":\"ShareModal_shareIcons__2jsII\",\"shareUrl\":\"ShareModal_shareUrl__3yYU7\",\"shareCloseButton\":\"ShareModal_shareCloseButton__3f9lo\",\"skeleton-loading\":\"ShareModal_skeleton-loading__qyqqK\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"home_container__3vpxv\",\"firstPanelRow\":\"home_firstPanelRow__3K0sw\",\"panel\":\"home_panel__3hnxs\",\"panels\":\"home_panels__8qgw_\",\"body\":\"home_body__1dMQA\",\"sorry\":\"home_sorry__2LDi-\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"error\":\"ActivateCard_error__n7Pcd\",\"container\":\"ActivateCard_container__3Wupj\",\"fourLast\":\"ActivateCard_fourLast__1tGfu\",\"button\":\"ActivateCard_button__2yOme\",\"skeleton-loading\":\"ActivateCard_skeleton-loading__18y7f\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"AddBank_main__1Z4AQ\",\"optionBox\":\"AddBank_optionBox__3n7BU\",\"skeleton-loading\":\"AddBank_skeleton-loading__1zdts\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"VerifyMicroDeposits_main__3PL2Q\",\"error\":\"VerifyMicroDeposits_error__wPH7o\",\"done\":\"VerifyMicroDeposits_done__8lMd8\",\"checkmark\":\"VerifyMicroDeposits_checkmark__2usWp\",\"skeleton-loading\":\"VerifyMicroDeposits_skeleton-loading__B0xHn\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"ConfirmPayment_main__OVV3L\",\"paidMain\":\"ConfirmPayment_paidMain__3RI7B\",\"button\":\"ConfirmPayment_button__mQtM6\",\"checkMark\":\"ConfirmPayment_checkMark__1VV3L\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"AddPhone_container__2iiQf\",\"serverError\":\"AddPhone_serverError__T3WiW\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"DownloadApp_container__yf3Ve\",\"button\":\"DownloadApp_button__2llpp\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"PopupView_container__eFV52\",\"header\":\"PopupView_header__36JwN\",\"contentFrame\":\"PopupView_contentFrame__2FSCF\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"signupbar_container__CJKED\",\"column\":\"signupbar_column__3UUnR\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"Statements_main__Zdwpa\",\"row\":\"Statements_row__3J2M2\",\"month\":\"Statements_month__16s3P\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"infoBanner\":\"InfoBanner_infoBanner__2G-G0\",\"text\":\"InfoBanner_text__1E8q1\",\"closeButton\":\"InfoBanner_closeButton__1TumD\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"PullingCredit_container__13ETh\",\"spinnerDiv\":\"PullingCredit_spinnerDiv__2mMMK\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"Verifying_container__j0JX9\",\"spinnerDiv\":\"Verifying_spinnerDiv__HGViu\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"NavBody_container__1xQsr\",\"body\":\"NavBody_body__3TL7d\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"ReferAFriend_main__OAwbQ\",\"inviteCode\":\"ReferAFriend_inviteCode__Ez9x5\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"ChangePassword_main__3W_5g\",\"serverError\":\"ChangePassword_serverError__1Cyjy\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"AddInstant_main__3EbIy\",\"error\":\"AddInstant_error__1oZPW\",\"skeleton-loading\":\"AddInstant_skeleton-loading__60Aoo\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"mainDiv\":\"ChromeExtension_mainDiv__1Qjfm\",\"body\":\"ChromeExtension_body__36V_J\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"mainDiv\":\"App_mainDiv__3FDq1\",\"body\":\"App_body__2a4FT\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"authDiv\":\"auth_authDiv__224LV\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"mainBox\":\"TransactionFilter_mainBox__IdeSd\",\"reset\":\"TransactionFilter_reset__35p2E\",\"searchBox\":\"TransactionFilter_searchBox__3OjAO\",\"searchIcon\":\"TransactionFilter_searchIcon__ASsJA\",\"twoColumns\":\"TransactionFilter_twoColumns__1DvbF\",\"column\":\"TransactionFilter_column__QSFhr\",\"select\":\"TransactionFilter_select__2eo5N\",\"selectedText\":\"TransactionFilter_selectedText__2mdyJ\",\"options\":\"TransactionFilter_options__ub4_b\",\"cardTerminated\":\"TransactionFilter_cardTerminated__3WZWm\",\"itemSelected\":\"TransactionFilter_itemSelected__2umzD\",\"actionBar\":\"TransactionFilter_actionBar__2MUDL\",\"button\":\"TransactionFilter_button__3kn0L\",\"buttonApply\":\"TransactionFilter_buttonApply__3VTol\",\"skeleton-loading\":\"TransactionFilter_skeleton-loading__1Fwku\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"PreApproved_container__1CKy1\",\"boldText\":\"PreApproved_boldText__3vWRC\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"ldsEllipsis\":\"LoadingAnimation_ldsEllipsis__1p472\",\"lds-ellipsis1\":\"LoadingAnimation_lds-ellipsis1__3co3n\",\"lds-ellipsis2\":\"LoadingAnimation_lds-ellipsis2__1sKej\",\"lds-ellipsis3\":\"LoadingAnimation_lds-ellipsis3__2qOcG\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"StateNotAvailable_container__1Onnw\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"applyDiv\":\"apply_applyDiv__30-5X\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"plaidButton\":\"plaidbutton_plaidButton__24uS1\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"MiniAppFullScreen_main__25VTo\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"AddCard_main__2vMxB\",\"container\":\"AddCard_container__m_69w\",\"backArrow\":\"AddCard_backArrow__2seZX\",\"select\":\"AddCard_select__8_8Mt\",\"option\":\"AddCard_option__2zCYt\",\"form\":\"AddCard_form__2Szkq\",\"serverError\":\"AddCard_serverError__IULFL\",\"spendingLimitDiv\":\"AddCard_spendingLimitDiv__2M6ZJ\",\"hideInput\":\"AddCard_hideInput__NZ3pm\",\"spendingLimitSelect\":\"AddCard_spendingLimitSelect__2hdl0\",\"colorDiv\":\"AddCard_colorDiv__2eWZv\",\"colorSelect\":\"AddCard_colorSelect__tzi0T\",\"colorRow\":\"AddCard_colorRow__10B7l\",\"cardColor\":\"AddCard_cardColor__2wsKG\",\"createButton\":\"AddCard_createButton__BqUsD\",\"selectType\":\"AddCard_selectType__2k57d\",\"selected\":\"AddCard_selected__1RflY\",\"shippingSpeed\":\"AddCard_shippingSpeed__PCOha\",\"shippingOption\":\"AddCard_shippingOption__3oWOM\",\"shippingSpeedValue\":\"AddCard_shippingSpeedValue__1WrBo\",\"buttonBar\":\"AddCard_buttonBar__3gAG3\",\"notapproved\":\"AddCard_notapproved__B1DIO\",\"addToWallet\":\"AddCard_addToWallet__2JUA2\",\"skeleton-loading\":\"AddCard_skeleton-loading__1RegJ\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"MiniappDetails_main__rupM0\",\"header\":\"MiniappDetails_header__3yNQh\",\"icon\":\"MiniappDetails_icon__3LZ7k\",\"button\":\"MiniappDetails_button__i1gC9\",\"notAvailable\":\"MiniappDetails_notAvailable__2crlI\",\"toolbar\":\"MiniappDetails_toolbar__12wXG\",\"toolbutton\":\"MiniappDetails_toolbutton__1V5hw\",\"install\":\"MiniappDetails_install__jLM-3\",\"screenshots\":\"MiniappDetails_screenshots__Ov_mS\",\"madeBy\":\"MiniappDetails_madeBy__3NB9H\",\"comments\":\"MiniappDetails_comments__3aff7\",\"comment\":\"MiniappDetails_comment__2fX9N\",\"commentText\":\"MiniappDetails_commentText__1CLJ_\",\"createdAt\":\"MiniappDetails_createdAt__2Ssn_\",\"author\":\"MiniappDetails_author__1OO9l\",\"addComment\":\"MiniappDetails_addComment__2UNtP\",\"commentModal\":\"MiniappDetails_commentModal__15NJt\",\"commentCancel\":\"MiniappDetails_commentCancel__3oFAJ\",\"commentSave\":\"MiniappDetails_commentSave__2ors1\",\"skeleton-loading\":\"MiniappDetails_skeleton-loading__tiSm-\"};","import { SVGProps } from \"react\"\n\ninterface LogoProps extends SVGProps {\n backgroundColor?: string\n color?: string\n}\n\nexport const Logo = ({ backgroundColor = \"transparent\", color = \"black\", ...props }: LogoProps): JSX.Element => (\n \n \n \n)\n","import { AccountService, AuthService, GraphQLException, SMSChallenge } from \"@klutch-card/klutch-js\"\nimport React, { ChangeEvent, FormEvent, useRef, useState } from \"react\"\nimport { useHistory, useLocation, } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport {KTextInput} from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport { Logo } from \"../../components/Logo\"\nimport qs from \"qs\"\n\nimport classes from \"./Signup.module.scss\"\nimport { useCookies } from \"react-cookie\"\n\nexport interface LoginPageProps {\n redirectTo?: string\n}\n\nconst LoginPage: React.FC = ({redirectTo}) => {\n\n const loc = useLocation()\n const query = qs.parse(loc.search, {ignoreQueryPrefix: true})\n\n const [email, setEmail] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [code, setCode] = useState(\"\")\n\n const [authChallenge, setAuthChallenge] = useState()\n\n \n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n\n const inputs = [useRef(null), useRef(null),useRef(null)]\n \n const history = useHistory()\n\n \n\n const handleSubmit = async (e: FormEvent) => {\n setServerError(\"\")\n setLoading(true)\n e.preventDefault()\n const validated = inputs.map( i => {\n const v = i.current?.validate(true)\n if (!i.current) {\n return true\n }\n\n return v == ValidationState.VALID\n }).reduce((prev, curr) => prev && curr)\n \n if (!validated) {\n setLoading(false)\n return false\n }\n try {\n let authResult\n if (code && authChallenge) {\n authResult = await AuthService.respondToChallenge(authChallenge, code)\n } else {\n authResult = await AuthService.signIn(email, password) \n } \n if (\"ChallengeName\" in authResult) {\n setLoading(false)\n setAuthChallenge(authResult)\n return\n }\n if (query && query[\"redirectTo\"]) {\n history.replace(query[\"redirectTo\"] as string)\n } else {\n if (redirectTo) {\n history.replace(redirectTo)\n } else {\n history.replace(\"/apply\") \n } \n }\n \n } catch (e) {\n if (e instanceof GraphQLException) {\n const [alloyException] = e.getAlloyCoreExceptionType()\n if (alloyException == \"com.alloycard.core.entities.DuplicateEntityException\") {\n setServerError(\"An account with this email address already exists\")\n setLoading(false)\n return\n } \n if (alloyException == \"com.alloycard.core.entities.user.InvalidInviteCodeException\") {\n setServerError(\"Invalid Referral Code\")\n setLoading(false)\n return\n }\n } \n setServerError((e as Error).message)\n setLoading(false)\n } \n \n }\n\n\n\n\n const handlePasswordChanged = (event: ChangeEvent) => { \n const text = event.target?.value \n setPassword(text)\n \n }\n\n const userNameOrAuthChallenge = () => {\n if (!authChallenge) {\n return (\n <>\n setEmail(event.target.value)} \n ref={inputs[0]} \n validations={[\n {type: ValidationType.required, errorMessage: \"Email is required\"},\n {type: ValidationType.regex, \n config: /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/,\n errorMessage: \"Please enter a valid email address\"}\n ]} \n /> \n \n \n )\n }\n else {\n return (\n <>\n setCode(event.target.value)} \n ref={inputs[2]} \n validations={[\n {type: ValidationType.required, errorMessage: \"Code is required\"} \n ]} \n /> \n \n )\n }\n }\n\n return (\n
\n
\n \n

Welcome to Klutch! Please login below.

\n
\n
\n
{serverError} 
\n {userNameOrAuthChallenge()}\n LOGIN \n Forgot your password?\n
\n \n Do not have an account? Click here to Sign up\n \n \n By signing in, you accept our Terms and Conditions and acknowledge you have read the Klutch Privacy Policy\n \n\n \n Version: 2.1.16\n \n
\n )\n}\n\n\nexport default LoginPage","import React from \"react\"\nimport { ChangeEvent, useState } from \"react\"\nimport {KTextInput} from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationType } from \"@klutchcard/klutch-webcomponents\"\n\n\nimport classes from \"./NewPasswordInput.module.scss\"\n\nexport interface NewPasswordProps {\n value: string,\n onChange: (text: string) => void \n label?: string\n}\n\n\nexport const NewPasswordInput = React.forwardRef(({value, onChange, label}: NewPasswordProps, ref) => {\n\n const [passwordReqs, setPassswordReqs] = useState([\"\", \"\", \"\"])\n\n const handlePasswordChanged = (event: ChangeEvent) => { \n const text = event.target?.value \n onChange(text)\n let newReqs = [classes.passwordreqsRed, classes.passwordreqsRed, classes.passwordreqsRed]\n if (text.length == 0) {\n newReqs = [\"\", \"\", \"\"]\n }\n if (text.length >= 8) {\n newReqs[2] = classes.passwordreqsGreen\n } \n if (/(?=.*[a-z])(?=.*[A-Z]).*/.test(text)) {\n newReqs[1] = classes.passwordreqsGreen\n }\n if (/(?=.*[A-Za-z])(?=.*[0-9])/.test(text)) {\n newReqs[0] = classes.passwordreqsGreen\n }\n setPassswordReqs(newReqs) \n \n }\n \n return (\n <>\n \n
    \n
  • Must include letters and numbers
  • \n
  • Must include lower and upper cases
  • \n
  • Must be at lest 8 characters
  • \n
\n \n )\n})","import { AccountService, AuthService, GraphQLException } from \"@klutch-card/klutch-js\"\nimport React, { ChangeEvent, FormEvent, useRef, useState } from \"react\"\nimport { useHistory, useLocation, } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport {KTextInput} from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport { Logo } from \"../../components/Logo\"\nimport qs from \"qs\"\n\nimport classes from \"./Signup.module.scss\"\nimport { useCookies } from \"react-cookie\"\nimport { NewPasswordInput } from \"./NewPasswordInput\"\n\nimport * as Sentry from \"@sentry/browser\";\n\nconst ForgotPassword: React.FC = () => {\n\n const loc = useLocation()\n\n const [email, setEmail] = useState(\"\")\n const [code, setCode] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n\n \n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n const [emailSent, setEmailSent] = useState(false)\n\n const inputs = [useRef(null)]\n const passwordinput = useRef(null)\n \n const history = useHistory()\n\n\n const handleSubmit = async (e: FormEvent) => {\n setLoading(true)\n e.preventDefault()\n const validated = inputs.map( i => {\n const v = i.current?.validate(true)\n return v == ValidationState.VALID\n }).reduce((prev, curr) => prev && curr)\n \n if (!validated) {\n setLoading(false)\n return false\n }\n try {\n await AuthService.forgotPassword(email)\n setEmailSent(true) \n setLoading(false) \n \n } catch (e) {\n setServerError((e as Error).message)\n setLoading(false)\n } \n \n }\n\n\n const handleCodeSubmit = async (e: FormEvent) => {\n setLoading(true)\n e.preventDefault()\n const validated = passwordinput?.current?.validate(true)\n \n if (!validated) {\n setLoading(false)\n return false\n }\n try {\n await AuthService.resetPassword(email, code, password)\n history.replace(\"/auth/login\")\n } catch (e: any) {\n console.error(e)\n if (e.response.data.__type == \"CodeMismatchException\") {\n setServerError(\"Invalid Code, please check your email and try again\")\n return\n }\n if (e.response.data.__type == \"LimitExceededException\") {\n setServerError(\"You tried too many times, please try again later.\")\n return\n }\n setServerError(\"An error occurred, please contact support at support@klutchcard.com\")\n Sentry.captureException(e);\n \n } finally {\n setLoading(false)\n }\n \n \n }\n\n\n\n\n if (emailSent) {\n return (\n
\n
\n \n

If there's an account, we just sent an email to you. Please enter the code you received on your email:

\n
\n
\n
{serverError} 
\n setCode(event.target.value)} \n ref={inputs[0]} \n /> \n setPassword(text)}\n ref={passwordinput}\n\n /> \n LOGIN \n \n
) \n }\n\n return (\n
\n
\n \n

Please type your email below, we will send you an email to reset your password.

\n
\n
\n
{serverError} 
\n setEmail(event.target.value)} \n ref={inputs[0]} \n validations={[\n {type: ValidationType.required, errorMessage: \"Email is required\"},\n {type: ValidationType.regex, \n config: /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/,\n errorMessage: \"Please enter a valid email address\"}\n ]} \n /> \n SUBMIT \n \n\n
\n )\n}\n\n\nexport default ForgotPassword","import { AuthService, User, UserService } from \"@klutch-card/klutch-js\"\nimport { useEffect, useState } from \"react\"\nimport { useLocation } from \"react-router-dom\"\n\nimport * as Sentry from \"@sentry/browser\"\n\nlet currentUser: User | undefined = undefined\n\nexport const Analytics = {\n \n startAnalytics: (): void => {\n const loc = useLocation()\n const [isAuthenticated, setIsAuthenticated] = useState(false)\n\n useEffect(() => {\n console.log(\"CurrentPage :>> \", loc.pathname)\n\n let vPath = loc.pathname\n \n if (vPath.startsWith(\"/cards/\")) {\n vPath = vPath.replace(/\\/cards(\\/[a-f0-9-]+)?(.*)/gm, \"/cards/details$2\")\n }\n if (vPath.startsWith(\"/transactions/\")) {\n vPath = vPath.replace(/\\/transactions(\\/[a-f0-9-]+)?(.*)/gm, \"/transactions/details$2\")\n }\n if (vPath.startsWith(\"/transfersources/\")) {\n vPath = vPath.replace(/\\/transactions(\\/[a-f0-9-]+)?(.*)/gm, \"/transfersources/details$2\")\n }\n\n if (loc.pathname == \"/\") { //do not log \"/\" to not confuse with marketing website\n return\n }\n\n\n window.dataLayer.push({\n \"event\": \"page_changed\",\n \"page_path\": loc.pathname, \n \"vPath\": vPath,\n \"user_id\": currentUser?.id\n })\n }, [loc.pathname])\n\n useEffect(() => {\n const subscription = AuthService.addSubscriber(auth => {\n const run = async () => {\n await (new Promise(e => { setTimeout(() => { e(1) }, 3000) }))\n setIsAuthenticated(!!auth?.AccessToken)\n }\n run()\n })\n return () => subscription.unsubscribe()\n }, [])\n\n useEffect(() => {\n const fun = async () => {\n if (!isAuthenticated) {\n return\n }\n\n\n const subs = UserService.addSubscriber(user => { \n if (user) {\n currentUser = user\n window.dataLayer.push({\n event: \"app_identify\", \n \"user_id\": user?.id,\n email: user?.email, \n firstName: user?.firstName, \n lastName: user?.lastName,\n phone: user?.phone \n })\n Sentry.setUser({ id: user?.id, email: user?.email })\n\n }\n })\n\n return () => subs.unsubscribe()\n }\n fun()\n }, [isAuthenticated])\n }, \n\n pushEvent: (eventName: string): void => {\n const layerObj = {\n \"event\": eventName\n }\n console.log(\"LayerObj\", layerObj)\n window.dataLayer.push(layerObj)\n }\n\n}\n\n\n\n\n\n","import { AccountService, AuthService, GraphQLException } from \"@klutch-card/klutch-js\"\nimport React, { ChangeEvent, FormEvent, useRef, useState } from \"react\"\nimport { useHistory, useLocation, } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport {KTextInput} from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport { Logo } from \"../../components/Logo\"\nimport qs from \"qs\"\n\nimport classes from \"./Signup.module.scss\"\nimport { useCookies } from \"react-cookie\"\nimport { Analytics } from \"../../services/analytics\"\n\n\nconst ProspectPage: React.FC = () => {\n\n const loc = useLocation()\n const query = qs.parse(loc.search, {ignoreQueryPrefix: true})\n\n const [firstName, setFirstName] = useState(\"\")\n const [lastName, setLastName] = useState(\"\")\n const [email, setEmail] = useState(\"\")\n const [referralCode, setReferralcode] = useState(query[\"referralCode\"] as string|| \"\")\n\n \n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n\n const inputs = [useRef(null), useRef(null), useRef(null)]\n \n const [cookies, setCookie, removeCookie] = useCookies([\"klutch_attribution\"]);\n\n const [prospectCreated, setProspectCreated] = useState(false)\n\n const history = useHistory()\n \n\n\n const handleSubmit = async (e: FormEvent) => {\n setLoading(true)\n e.preventDefault()\n const validated = inputs.map( i => {\n const v = i.current?.validate(true)\n return v == ValidationState.VALID\n }).reduce((prev, curr) => prev && curr)\n \n if (!validated) {\n setLoading(false)\n return false\n }\n try {\n await AuthService.createProspect(referralCode, firstName, \"\", lastName, email)\n \n const attr = cookies[\"klutch_attribution\"]\n if (attr) {\n await AccountService.addExtraData({ \n klutchAttributionCookie: attr || \"\",\n userAgent: window.navigator.userAgent || \"\" })\n } else {\n await AccountService.addExtraData({ \n userAgent: window.navigator.userAgent || \"\" })\n }\n\n \n Analytics.pushEvent(\"app_apply_prospect_created\"); \n setProspectCreated(true)\n } catch (e) {\n if (e instanceof GraphQLException) {\n const [alloyException] = e.getAlloyCoreExceptionType()\n if (alloyException == \"com.alloycard.core.entities.DuplicateEntityException\") {\n setServerError(\"EMAIL_EXISTS\")\n setLoading(false)\n return\n } \n if (alloyException == \"com.alloycard.core.entities.user.InvalidInviteCodeException\") {\n setServerError(\"Invalid Referral Code\")\n setLoading(false)\n return\n }\n } \n setServerError((e as Error).message)\n setLoading(false)\n } \n \n }\n\n\n\n\n const renderBody = () => {\n if (prospectCreated) {\n return (\n
\n \n Thank you for your interest in Klutch.

\n We will be in touch when it's your turn to apply for the card. \n
\n window.location.href = \"https://www.klutchcard.com\"}>BACK TO HOME \n
\n )\n \n } else {\n\n return (\n
\n {serverError === \"EMAIL_EXISTS\" ? (\n
It looks like you already have an account. Click here to login
\n ) : (\n
{serverError} 
\n )}\n \n setFirstName(event.target.value)}\n ref={inputs[0]}\n validations={[\n {type: ValidationType.required, errorMessage: \"First name is a required field\"},\n {type: ValidationType.minLength, config: 2, errorMessage: \"Must contain at least 2 characters\"},\n {type: ValidationType.regex, config: /^([A-Za-z-\\s])*$/g, errorMessage: \"Please enter a valid name\"}\n ]} \n />\n setLastName(event.target.value)} \n ref={inputs[1]}\n validations={[\n {type: ValidationType.required, errorMessage: \"Last name is a required field\"},\n {type: ValidationType.minLength, config: 2, errorMessage: \"Must contain at least 2 characters\"},\n {type: ValidationType.regex, config: /^([A-Za-z-\\s])*$/g, errorMessage: \"Please enter a valid name\"}\n ]}\n\n /> \n setEmail(event.target.value)} \n ref={inputs[2]} \n validations={[\n {type: ValidationType.required, errorMessage: \"Email is required\"},\n {type: ValidationType.regex, \n config: /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/,\n errorMessage: \"Please enter a valid email address\"}\n ]} \n /> \n setReferralcode(event.target.value?.toLocaleUpperCase())} \n /> \n JOIN THE WAITLIST \n \n )\n\n }\n }\n\n\n\n return (\n
\n
\n \n

Welcome to Klutch! Join our waitlist below.

\n
\n {renderBody()}\n \n By joining, you accept our Terms and Conditions and acknowledge you have read the Klutch Privacy Policy\n \n \n Version: 2.1.15\n \n
\n )\n}\n\n\nexport default ProspectPage","import { Redirect, Route, Switch } from \"react-router-dom\"\nimport SignUpPage from \"./SignUp\"\nimport classes from \"./index.module.scss\"\nimport { useParams } from \"react-router\"\nimport LoginPage from \"./Login\"\nimport ForgotPassword from \"./ForgotPassword\"\nimport ProspectPage from \"./Prospect\"\n\nexport const AuthRouter = (): JSX.Element => {\n const { code } = useParams<{ code: string }>()\n\n return (\n
\n \n \n \n \n \n \n \n \n \n \n\n \n\n \n
\n )\n}\nexport default AuthRouter","const throwMissingVariable = (name: string) => { throw new Error(`missing required env variable \"${name}\"`) }\n\nexport default {\n klutchServerUrl: process.env.REACT_APP_KlutchServerUrl || throwMissingVariable(\"REACT_APP_KlutchServerUrl\"),\n userPoolClientId: process.env.REACT_APP_UserPoolClientId || throwMissingVariable(\"REACT_APP_UserPoolClientId\"),\n userPoolServer: process.env.REACT_APP_UserPoolServer || throwMissingVariable(\"REACT_APP_UserPoolServer\"), \n}\n","\nimport { Address } from \"@klutch-card/klutch-js\"\nimport { KTextInput, KTextInputProps } from \"@klutchcard/klutch-webcomponents\"\nimport { usePlacesWidget } from \"react-google-autocomplete\"\n\n\nexport interface KPlacesAutoCompleteInputProps extends KTextInputProps {\n onAddressCompleted?: (address: Address) => void \n}\n\nexport const KPlacesAutoCompleteInput = (props: KPlacesAutoCompleteInputProps): JSX.Element => {\n const { ref } = usePlacesWidget({\n apiKey:\"AIzaSyBKiB-yB8m3TLnsoIeiU-umpUnMuvxQe-U\",\n options: {\n types: [\"address\"],\n componentRestrictions: { country: \"us\" },\n },\n onPlaceSelected: (place) => { \n \n const addr =place.address_components\n if (addr) {\n const streetNumber = addr.find((c: any) => c.types.includes(\"street_number\"))?.long_name\n const street = addr.find((c:any) => c.types.includes(\"route\"))?.long_name\n const complement = addr.find((c: any) => c.types.includes(\"subpremise\"))?.long_name\n const city = addr.find((c:any) => c.types.includes(\"locality\"))?.long_name\n const state = addr.find((c:any) => c.types.includes(\"administrative_area_level_1\"))?.short_name\n const zipCode = addr.find((c:any) => c.types.includes(\"postal_code\"))?.long_name\n const address = new Address({streetNumber, street, complement, city, state, zipCode})\n props.onAddressCompleted && props.onAddressCompleted(address) \n }\n }\n });\n //@ts-ignore: type of inputs are the same\n return ()\n }\n\n\n\n\n\n\n\n","import { Account, AccountService, User, UserService } from \"@klutch-card/klutch-js\"\nimport { useEffect, useState } from \"react\"\n\nexport function useAccount(): Account | null | undefined {\n const [account, setAccount] = useState()\n\n useEffect(() => {\n const s = AccountService.addSubscriber(a => {\n setAccount(a)\n })\n return () => s.unsubscribe()\n }, [])\n\n return account\n}\n\nexport function useUser(): User | null | undefined {\n const [user, setUser] = useState()\n\n useEffect(() => {\n const s = UserService.addSubscriber(u => {\n setUser(u)\n })\n return () => s.unsubscribe()\n }, [])\n\n return user\n}\n\n","import { AccountService, Address, USState } from \"@klutch-card/klutch-js\"\nimport React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { KPlacesAutoCompleteInput } from \"../../components/KTextInput\"\nimport {KTextInput} from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport classes from \"./AddAddress.module.scss\"\nimport { Analytics } from \"../../services/analytics\"\nimport { useAccount, useUser } from \"../../services/klutchHooks\"\n\nexport const availableStates = [\"AK\", \"AL\", \"AR\", \"AZ\", \"CA\", \"CO\", \"CT\", \"DC\", \"DE\", \"FL\", \"GA\", \"HI\", \"IA\", \"ID\", \"IL\", \"KS\", \"LA\", \"MA\", \"MD\", \"ME\", \"MN\", \"MO\", \"MT\", \"NC\", \"ND\", \"NE\", \"NH\", \"NJ\", \"NM\", \"NY\", \"OH\", \"OK\", \"OR\", \"PA\", \"RI\", \"SC\", \"SD\", \"TN\", \"TX\", \"UT\"]\n\nconst AddAddress: React.FC = () => {\n\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n const [street, setStreet] = useState(\"\")\n const [complement, setComplement] = useState(\"\")\n const [city, setCity] = useState(\"\")\n const [state, setState] = useState(\"\")\n const [zipCode, setZipCode] = useState(\"\")\n const [showEdit, setShowEdit] = useState(true)\n\n const [valid, setValid] = useState(ValidationState.PRISTINE)\n\n const inputs = [useRef(null), useRef(null), useRef(null), useRef(null),useRef(null),]\n\n const history = useHistory()\n const apt = useRef()\n\n const account = useAccount()\n const user = useUser()\n\n const address = account?.address\n\n const addressCompleted = (address: Address) => {\n setStreet(`${address.streetNumber || \"\"} ${address.street}`)\n setComplement(address.complement || \"\")\n setCity(address.city)\n setState(address.state)\n setZipCode(address.zipCode || \"\") \n }\n\n\n useEffect(() => {\n if (address?.state) {\n setStreet(`${address.streetNumber || \"\"} ${address.street}`)\n setComplement(address.complement || \"\")\n setCity(address.city)\n setState(address.state)\n setZipCode(address.zipCode || \"\") \n setShowEdit(false)\n }\n }, [address?.state])\n\n \n \n const stateList = Object.keys(USState)\n\n\n const decideNext = () => {\n let to = \"/apply/ssn\"\n if (user?.hasSSN) {\n to = \"/apply/annual-income\"\n }\n if (!availableStates.includes(state.toUpperCase())) {\n to = \"/apply/state-not-available\"\n } \n\n Analytics.pushEvent(\"app_apply_address_added\"); \n history.push(to)\n }\n\n\n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true) \n const validated = inputs.map( i => {\n const v = i.current?.validate(true) \n if (v === undefined) {\n return true\n }\n return v == ValidationState.VALID\n }).reduce((prev, curr) => prev && curr)\n\n if (!validated) {\n setLoading(false)\n return\n }\n \n\n const sn = (street.match(/^\\d+/gm) ?? [\"\"])[0] \n const s = street.substr(street.indexOf(sn) + sn.length).trim()\n\n const fullAddress = `${sn} ${s} ${complement}`\n\n const pobox = /^\\s*(.*((p|post)[-.\\s]*(o|off|office)[-.\\s]*(b|box|bin)[-.\\s]*)|.*((p|post)[-.\\s]*(o|off|office)[-.\\s]*)|.*((p|post)[-.\\s]*(b|box|bin)[-.\\s]*)|(box|bin)[-.\\s]*)(#|n|num|number)?\\s*\\d+/i;\n if (fullAddress.match(pobox) != null) {\n setServerError(\"PO BOXes are not accepted. Please use your residential address.\")\n setLoading(false)\n return\n }\n\n try {\n await AccountService.changeAddress(sn, s, complement || \"\", city, state, zipCode)\n decideNext()\n \n } catch (e: any) {\n setServerError(e.message)\n setLoading(false) \n }\n\n }\n\n if (!showEdit) {\n return (\n
\n

Primary Address

\n

We have identified your current location as your place of residence. Please check and correct if needed

\n
\n \n {address?.streetNumber} {address?.street}\n \n {address?.complement}\n \n {address?.city}\n \n {address?.state}\n
\n
\n setShowEdit(true)}>CHANGE\n decideNext() }>LOOKS GOOD\n
\n \n
\n )\n }\n\n\n return (\n
\n

Primary Address

\n
Your card will be shipped to this address
\n
\n
{serverError} 
\n setStreet(e.target.value)}\n ref={inputs[0]}\n validations={[\n {type: ValidationType.required, errorMessage: \"Street is a required field\"},\n {type: ValidationType.minLength, config: 3, errorMessage: \"Must contain at least 3 characters\"},\n {type: ValidationType.regex, config: /^([A-Z0-9a-z\\s])*$/g, errorMessage: \"Please enter a valid name\"}\n ]} \n />\n setComplement(e.target.value)} \n validations={[\n {type: ValidationType.regex, config: /^([A-Z0-9a-z\\s])*$/g, errorMessage: \"Please enter a valid name\"}\n ]} />\n setCity(e.target.value)}\n ref={inputs[2]}\n validations={[\n {type: ValidationType.required, errorMessage: \"City is a required field\"},\n {type: ValidationType.minLength, config: 3, errorMessage: \"Must contain at least 3 characters\"},\n {type: ValidationType.regex,\n config: /^([A-Za-z\\s])*$/g,\n errorMessage: \"Please enter a valid city name\"\n }\n ]} />\n setState(e.target.value)}\n ref={inputs[3]}\n maxLength={2}\n validations={[\n {type: ValidationType.required, errorMessage: \"State is a required field\"},\n {type: ValidationType.list, config: stateList, errorMessage: \"Invalid State\"},\n {type: ValidationType.regex, config: /^([A-Za-z\\s])*$/g, errorMessage: \"Please enter a valid name\"}\n ]} />\n setZipCode(e.target.value)}\n ref={inputs[4]}\n validations={[\n {type: ValidationType.required, errorMessage: \"Zip is a required field\"},\n {type: ValidationType.regex, config: /^(\\d{5}(?:-\\d{4})?)$/gm, errorMessage: \"Zip format is invalid\"}]} /> \n\n VERIFY \n \n
\n )\n}\n\n\nexport default AddAddress","import React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { KChangeEvent, KTextInput } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport classes from \"./AddSSN.module.scss\"\nimport { AccountService, UserService } from \"@klutch-card/klutch-js\"\nimport {DateTime} from \"luxon\"\nimport { Analytics } from \"../../services/analytics\"\n\nconst AddAnnualIncome : React.FC = () => {\n\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n\n const [income, setIncome] = useState(\"\")\n\n\n const history = useHistory()\n\n\n const [cardType, setCardType] = useState(\"UNSECURED\")\n\n\n useEffect(() => {\n (async () => {\n const cardType = await AccountService.getExtraData(\"cardType\")\n setCardType(cardType) \n })()\n }, [])\n \n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n\n\n if (!income || income.trim() == \"\") {\n setLoading(false)\n return\n }\n try { \n const incomeNumber = parseFloat(income.replace(/,/g, \"\"))\n if (!incomeNumber || incomeNumber < 0 || incomeNumber > 9_999_999 ) {\n setServerError(\"Invalid Income\")\n setLoading(false)\n return\n }\n await AccountService.addExtraData({ income: incomeNumber })\n } catch (e) {\n setServerError((e as Error).message) \n setLoading(false)\n }\n Analytics.pushEvent(\"app_apply_income_added\"); \n \n history.push(\"/apply/verifying\")\n }\n\n const changeIncome = (e: KChangeEvent) => {\n const value = e.target.value\n const currentValue = value.replace(/[^\\d]/g, \"\")\n\n setIncome(previousValue => {\n if (!previousValue || value.length > previousValue.length) {\n return currentValue.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n }\n return \"\" \n } )\n }\n\n\n return (\n
\n

Annual Income

\n
Please provide your estimated total annual income including all sources of income. Include shared income not earned by you that is regularly deposited into your individual or joint account.
\n
\n
{serverError} 
\n \n {cardType == \"UNSECURED\" && (\n <>\n
Your Consent
\n
\n You understand that by clicking on “submit” button below,\n you are providing 'written instructions' to us under the Fair Credit Reporting Act\n authorizing us to obtain information from your personal credit report or other information from Equifax.\n You authorize Klutch to obtain such information solely to conduct a pre-qualification for credit.\n
\n \n )} \n\n SUBMIT \n \n
\n )\n}\n\n\nexport default AddAnnualIncome","import React, { FormEvent, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport classes from \"./AddGoals.module.scss\"\nimport { AccountService } from \"@klutch-card/klutch-js\"\nimport { Analytics } from \"../../services/analytics\"\n\nconst OPTIONS = [\n \"Save on Subscriptions\",\n \"Understand my Spending\",\n \"Create and track a Budget\",\n \"Automate Actions to Save Time\",\n \"Protect my Data\",\n \"Manage my Split Bills\",\n \"Build my Own MiniApp\",\n \"Auto Categorize Expenses\",\n \"Track money flows in Personal and Business\",\n \"Add Controls on Spending\",\n \"Optimize Cash Flow\",\n \"Save Effort with Customized Features \",\n]\n\ninterface OptionProps {\n label: string\n isSelected: boolean\n onPress: () => void\n}\n\nconst Option: React.FC = ({ label, isSelected, onPress }) => (\n \n {label}\n \n)\n\nexport const AddGoals: React.FC = () => {\n const [selections, setSelections] = useState(new Set())\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n const history = useHistory()\n\n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n\n try {\n const goals = OPTIONS.filter((_, idx) => selections.has(idx))\n await AccountService.addExtraData({ goals })\n Analytics.pushEvent(\"app_apply_goals_added\"); \n history.push(\"/apply/verifying\")\n \n } catch (e) {\n console.error(\"Error on sending goals\", e)\n setServerError((e as Error).message)\n setLoading(false)\n }\n }\n\n return (\n
\n

Your Goals

\n
Klutch MiniApps are here to help you live your best life. Select all that apply.
\n
\n
{serverError} 
\n\n \n {OPTIONS.map((option, idx) =>\n {\n const clone = new Set(selections)\n clone.has(idx) ? clone.delete(idx) : clone.add(idx)\n\n setSelections(clone)\n }}\n />\n )}\n
\n\n SUBMIT\n \n \n )\n}\n","import { GraphQLException, UserService } from \"@klutch-card/klutch-js\"\nimport React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { KTextInput } from \"@klutchcard/klutch-webcomponents\"\nimport { KChangeEvent } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\n\nimport classes from \"./AddPhone.module.scss\"\nimport { Analytics } from \"../../services/analytics\"\nimport {DateTime} from \"luxon\"\nimport { useUser } from \"../../services/klutchHooks\"\nimport { Spinner } from \"react-activity\"\n\nconst AddPhone: React.FC = () => {\n\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n const [duplicatePhone, setDuplicatePhone] = useState(false)\n\n const [phone, setPhone] = useState(\"\")\n const [birthDate, setBirthDate] = useState(\"\")\n\n \n\n const history = useHistory()\n\n const inputs = [useRef(null), useRef(null)]\n \n const phoneField = useRef(null)\n\n const user = useUser()\n\n useEffect(() => {\n if (user && user.birthDate) {\n setBirthDate(DateTime.fromJSDate(user.birthDate).toFormat(\"MM/dd/y\"))\n }\n }, [user?.id])\n\n const handleSubmit = async (e: FormEvent) => { \n e.preventDefault()\n const valid = inputs.map( i => {\n const v = i.current?.validate(true) \n if (v === undefined) {\n return true\n }\n return v == ValidationState.VALID\n }).reduce((prev, curr) => prev && curr)\n if (!valid) { \n return\n }\n setLoading(true)\n \n try { \n const phoneNumber = phone.replace(/[^\\d]/g, \"\") \n\n const dob = DateTime.fromFormat(birthDate, \"MM/dd/y\", {zone: \"utc\"})\n await UserService.changeBirthDate(dob.toJSDate()) \n\n await UserService.changePhone(phoneNumber) \n Analytics.pushEvent(\"app_apply_phone_added\"); \n\n history.push(`/apply/verifyPhone?phone=${phoneNumber}`)\n } catch (e) {\n if (e instanceof GraphQLException) {\n const [alloyException] = e.getAlloyCoreExceptionType()\n if (alloyException == \"com.alloycard.core.entities.DuplicateEntityException\") {\n setDuplicatePhone(true)\n setLoading(false)\n return\n } \n } \n setServerError((e as Error).message) \n }\n \n \n }\n\n const normalizeBirthdateInput = (value: string, previousValue: string) => {\n if (!value) return value\n \n const currentValue = value.replace(/[^\\d]/g, \"\")\n const cvLength = currentValue.length \n \n if (!previousValue || value.length > previousValue.length) {\n\n if (cvLength < 2) return currentValue \n if (cvLength < 4) return `${currentValue.slice(0, 2)}/${currentValue.slice(2)}` \n return `${currentValue.slice(0, 2)}/${currentValue.slice(2, 4)}/${currentValue.slice(4, 8)}` \n }\n }\n\n const changeBirthDate = (e: KChangeEvent) => {\n const text = e.target.value\n\n setBirthDate(prev => {\n const normalized = normalizeBirthdateInput(text, prev) ?? text\n if (normalized.length >= 10) {\n phoneField.current?.focus()\n }\n return normalized\n }) \n }\n\n const changePhoneNumber = (event: KChangeEvent) => {\n\n const text: string = event.target.value\n \n if (!text) {\n setPhone(\"\")\n return\n }\n let currentValue = text.replace(/[^\\d]/g, \"\")\n if (text.startsWith(\"1\")) {\n currentValue = currentValue.slice(1)\n }\n if (text.startsWith(\"+1\")) {\n currentValue = currentValue.slice(1)\n }\n\n const cvLength = currentValue.length\n\n setPhone(prev => {\n let normalized = currentValue\n if (!prev || text.length > prev.length) {\n if (cvLength < 4) {\n normalized = currentValue\n } else if (cvLength < 7) {\n normalized = `(${currentValue.slice(0, 3)}) ${currentValue.slice(3)}`\n } else {\n normalized = `(${currentValue.slice(0, 3)}) ${currentValue.slice(3, 6)}-${currentValue.slice(6, 10)}`\n }\n }\n if (normalized.length >= 14) {\n// phoneField?.blur()\n }\n return normalized\n })\n }\n\n const adultAgo = new Date((Date.now() - (1000 * 60 * 60 * 24 * 365.25 * 18)))\n const oldestAgo = new Date((Date.now() - (1000 * 60 * 60 * 24 * 365.25 * 100)))\n\n\n\n return (\n
\n

PHONE AND BIRTH DATE

\n
\n
{serverError} 
\n {duplicatePhone && This phone is already associated with an account. Click here to login. }\n

We need to make sure you're you. Please let us know what number to send a code to

\n \n \n \n SEND CODE \n \n
\n )\n}\n\n\nexport default AddPhone","import React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { KChangeEvent, KTextInput } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport classes from \"./AddSSN.module.scss\"\nimport { AccountService, UserService } from \"@klutch-card/klutch-js\"\nimport { GraphQLException } from \"@klutch-card/klutch-js\"\n\nimport { Analytics } from \"../../services/analytics\"\n\n\nconst AddSSN: React.FC = () => {\n\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n\n const [ssn, setSSN] = useState(\"\")\n\n const inputs = [useRef(null)]\n const ssnField = useRef(null)\n\n const history = useHistory()\n \n const [showEdit, setShowEdit] = useState(true)\n\n const [cardType, setCardType] = useState(\"UNSECURED\")\n\n useEffect(() => {\n (async () => {\n const cardType = await AccountService.getExtraData(\"cardType\")\n setCardType(cardType) \n const last4 = await UserService.getLast4()\n \n setSSN(last4)\n if (last4) {\n setShowEdit(false)\n }\n })()\n }, [])\n\n\n\n const normalizeSSNInput = (value: string, previousValue: string) => {\n if (!value) return value \n \n const currentValue = value.replace(/[^\\d]/g, \"\")\n const cvLength = currentValue.length \n \n if (!previousValue || value.length > previousValue.length) {\n \n if (cvLength < 2) return currentValue \n if (cvLength < 6) return `${currentValue.slice(0, 3)}-${currentValue.slice(3)}` \n return `${currentValue.slice(0, 3)}-${currentValue.slice(3, 5)}-${currentValue.slice(5, 9)}` \n }\n }\n\n const changeSSN = (e: KChangeEvent) => {\n const text = e.target.value\n setSSN(prev => {\n const normalized = normalizeSSNInput(text, prev) ?? text\n\n return normalized \n })\n }\n\n\n\n\n\n\n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n\n const validated = inputs.map( i => {\n const v = i.current?.validate(true) \n if (v === undefined) {\n return true\n }\n return v == ValidationState.VALID\n }).reduce((prev, curr) => prev && curr)\n\n if (!validated) {\n setLoading(false)\n return\n }\n\n const goodSSN = ssn.replace(/[^\\d]/g, \"\").trim()\n if (!goodSSN || goodSSN.length != 9) {\n setServerError(\"Invalid SSN\")\n setLoading(false)\n return\n }\n try { \n\n await UserService.changeSSN(goodSSN)\n \n Analytics.pushEvent(\"app_apply_ssn_added\"); \n history.push(\"/apply/annual-income\")\n } catch (e) {\n if (e instanceof GraphQLException) {\n const [alloyException] = e.getAlloyCoreExceptionType()\n if (alloyException == \"com.alloycard.core.entities.DuplicateEntityException\") {\n setServerError(\"An account with this SSN already exists\")\n setLoading(false)\n return\n } \n } \n console.error(\"Error on sending SSN\", e)\n setServerError((e as Error).message) \n\n } \n setLoading(false)\n }\n\n const adultAgo = new Date((Date.now() - (1000 * 60 * 60 * 24 * 365.25 * 18)))\n const oldestAgo = new Date((Date.now() - (1000 * 60 * 60 * 24 * 365.25 * 100)))\n\n if (!showEdit) {\n return (\n
\n

Pre Approval

\n

{cardType == \"UNSECURED\" ? \"This will not impact your credit score. A hard pull on your credit score only happens after approval and accepting our terms and conditions.\" : \n \"This will not impact your credit score. We use this information to verify your identity.\"}

\n \n
\n lock \n \n ****-**-{ssn}\n
\n
\n {cardType == \"UNSECURED\" && (\n <>\n
Your Consent
\n
\n You understand that by clicking on “submit” button below,\n you are providing 'written instructions' to us under the Fair Credit Reporting Act\n authorizing us to obtain information from your personal credit report or other information from Equifax.\n You authorize Klutch to obtain such information solely to conduct a pre-qualification for credit.\n
\n \n )}\n\n
\n {setSSN(\"\"); setShowEdit(true)}} >CHANGE \n history.push(\"/apply/annual-income\")} >LOOKS GOOD \n
\n
\n )\n }\n\n\n return (\n
\n

Pre Approval

\n

This will not impact your credit score. A hard pull on your credit score only happens after approval and accepting our terms and conditions.

\n
\n
{serverError} 
\n\n
\n \n \n \n \n \n
\n
Your Consent
\n
\n You understand that by clicking on “submit” button below,\n you are providing 'written instructions' to us under the Fair Credit Reporting Act\n authorizing us to obtain information from your personal credit report or other information from Equifax.\n You authorize Klutch to obtain such information solely to conduct a pre-qualification for credit.\n
\n SUBMIT \n
\n
\n )\n}\n\n\nexport default AddSSN","import React, { FormEvent, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport classes from \"./AddGoals.module.scss\"\nimport { RevolvingLoanService } from \"@klutch-card/klutch-js\"\n\n\nexport const CreditFrozen: React.FC = () => {\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n const history = useHistory()\n\n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n\n try {\n await RevolvingLoanService.refreshReport()\n history.push(\"/apply/pulling-credit\")\n } catch (e: any) {\n setServerError(\"Please try again later\")\n }\n setLoading(false)\n }\n\n return (\n
\n

CREDIT FROZEN

\n
\n It looks like your credit is currently frozen\n
\n Please unfreeze your EQUIFAX credit report and press the retry button below.\n
\n\n
\n
{serverError} 
\n RETRY NOW\n
\n
\n )\n}\n","import React, { FormEvent, useEffect, useState } from \"react\"\nimport classes from \"./DownloadApp.module.scss\"\n\nimport { useCookies } from \"react-cookie\"\nimport { AuthService } from \"@klutch-card/klutch-js\";\nimport {KButton} from \"@klutchcard/klutch-webcomponents\";\nimport { Analytics } from \"../../services/analytics\";\n\n\nexport const DownloadApp: React.FC = () => {\n\n const [cookies, setCookie, removeCookie] = useCookies([\"provider\"]);\n const [token, setToken] = useState()\n const [loading, setLoading] = useState(false)\n \n useEffect(() => {\n Analytics.pushEvent(\"app_apply_downloadapp\"); \n AuthService.addSubscriber(auth => {\n setToken(auth?.RefreshToken)\n })\n \n }, [])\n\n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n if (window.ReactNativeWebView) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\"action\": \"goto\", \"page\": \"/apply/pre-approved\", \"auth\": token})) \n } \n setLoading(false)\n }\n\nif (cookies[\"provider\"] == \"mobile\") {\n return (\n
\n

You are in :)

\n
\n
Click continue to view your offer and create your first card!
\n

\n CONTINUE\n
\n
\n )\n}\n\nreturn (\n
\n

You are in :)

\n
\n
Download the Klutch App to get your first card and start spending!
\n
No need to sign up again, just log in.
\n

\n \n GO TO APP STORE\n \n
\n
\n )\n\n}","const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))\n\nconst retry = async (fn: () => Promise, maxRetries: number, delayMs = 500): Promise => {\n try {\n return await fn()\n } catch (err) {\n if (maxRetries <= 0) throw err\n\n console.log(`retry attempt ${maxRetries}`)\n await delay(delayMs)\n return await retry(fn, maxRetries - 1, delayMs)\n }\n}\n\nexport { delay, retry }\n","import { RevolvingLoan, RevolvingLoanService } from \"@klutch-card/klutch-js\"\nimport { useEffect, useState } from \"react\"\n\nexport default function useRevolvingLoan(callback?: (revolvingLoan: RevolvingLoan) => void, refreshCache = false) {\n const [revolvingLoan, setRevolvingLoan] = useState(null)\n \n useEffect(() => {\n const subscription = RevolvingLoanService.addSubscriber(r => {\n setRevolvingLoan(r)\n if (r) {\n if (callback) {\n callback(r)\n }\n }\n }, refreshCache) \n return () => subscription.unsubscribe()\n }, [revolvingLoan?.id, revolvingLoan?.approvalStatus])\n return revolvingLoan\n}","import { AccountService, Agreement, AgreementType } from \"@klutch-card/klutch-js\"\nimport React, { FormEvent, useEffect, useState } from \"react\"\nimport { Spinner } from \"react-activity\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { retry } from \"../../helper\"\n\nimport classes from \"./PreApproved.module.scss\"\nimport { Analytics } from \"../../services/analytics\"\nimport useRevolvingLoan from \"../../hooks/useRevolvingLoan\"\n\n\nexport const PreApproved: React.FC = () => {\n const [loading, setLoading] = useState(true)\n const [loanAgreement, setLoanAgreement] = useState()\n const history = useHistory()\n \n const revolvingLoan = useRevolvingLoan()\n\n useEffect(() => {\n const run = async () => {\n setLoading(true)\n Analytics.pushEvent(\"app_apply_preapproved\");\n let la: Agreement|undefined\n try {\n const agreements: Agreement[] = await retry(async () => {\n const a = await AccountService.getAgreements() \n la = a.find((x: Agreement) => x.agreementType === AgreementType.LOAN_AGREEMENT)\n if (!la) {\n throw new Error()\n }\n return la\n }, 6, 5000) \n \n if (!agreements) history.push(\"/apply/thankyou\")\n setLoanAgreement(la)\n setLoading(false)\n } catch (e) {\n history.push(\"/apply/thankyou\")\n }\n\n }\n run()\n \n \n }, [])\n\n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n \n history.push(\"/apply/download-app\") \n }\n\n return (\n
\n {loading ? (\n
\n \n
\n ) : (\n <>\n

You are Pre-Approved!

\n\n {revolvingLoan?.type == \"UNSECURED\" && (\n <>\n Success! By pressing “agree and continue” you are accepting these terms and Klutch will do a hard pull on credit score to issue your Klutch card. This may impact your credit score.\n

\n Terms & Conditions\n

\n Your credit limit is flexible. Unlike a traditional card with a set limit, the amount you can spend adapts based on factors such as your purchase, payment, payment, and credit history. The bank services are provided by Synapse's partner banks, Members FDIC. By accepting the terms of this loan on Klutch, you agree to Synapse's Consumer Loan Agreement and Consumer Cardholder Agreement.\n

\n Certifications\n

\n I certify that the SSN I provided is correct; I am not subject to backup withholding because: (a) I am exempt from backup withholding, or (b) I have not been notified by the Internal Revenue Service (IRS) that I am subject to backup withholding as a result of a failure to report all interest or dividends, or (c) the IRS has notified me that I am no longer subject to backup withholding; and I am a U.S. citizen.\n

\n \n )}\n {revolvingLoan?.type == \"SECURED\" && (\n <>\n Success! By pressing “agree and continue” you are accepting these terms.\n

\n Terms & Conditions\n

\n Your credit limit amoutn is your security deposit amount. Your security deposit will be returned to you when you graduate to our unsecured card or when you close your account. . The bank services are provided by Synapse's partner banks, Members FDIC. By accepting the terms of this loan on Klutch, you agree to Synapse's Consumer Loan Agreement and Consumer Cardholder Agreement.\n

\n Certifications\n

\n I certify that the SSN I provided is correct; I am not subject to backup withholding because: (a) I am exempt from backup withholding, or (b) I have not been notified by the Internal Revenue Service (IRS) that I am subject to backup withholding as a result of a failure to report all interest or dividends, or (c) the IRS has notified me that I am no longer subject to backup withholding; and I am a U.S. citizen.\n

\n \n )}\n\n
\n AGREE & CONTINUE\n
\n \n )}\n
\n )\n}\n","import React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { KChangeEvent, KTextInput } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport classes from \"./PullingCredit.module.scss\"\nimport { AccountService, ApprovalStatus, LoanApprovalStatus, UserService, RevolvingLoanService } from \"@klutch-card/klutch-js\"\nimport {DateTime} from \"luxon\"\nimport { Spinner } from \"react-activity\"\nimport \"react-activity/dist/library.css\";\nimport { retry } from \"../../helper\"\n\n\nconst PullingCredit : React.FC = () => {\n\n const history = useHistory()\n\n const decideNextStep = async () => {\n const revolvingLoan = await RevolvingLoanService.getRevolvingLoan() \n switch (revolvingLoan?.approvalStatus) {\n case LoanApprovalStatus.WAITING_AGREEMENT:\n history.replace(\"/apply/pre-approved\") \n break;\n case LoanApprovalStatus.WAITING_DEPOSIT:\n history.replace(\"/apply/security-deposit\")\n break; \n case LoanApprovalStatus.REJECTED:\n history.replace(\"/apply/choose-plan\")\n break;\n case LoanApprovalStatus.REPORT_FROZEN:\n history.push(\"/apply/report-frozen\")\n break;\n case LoanApprovalStatus.MANUAL_REVIEW:\n history.push(\"/apply/thankyou\")\n break; \n case LoanApprovalStatus.PENDING:\n history.push(\"/apply/thankyou\")\n break\n } \n }\n \n useEffect(() => {\n const run = async () => {\n try {\n const revolvingLoanStatus = await retry(async () => {\n console.log(\"retry attempt\")\n const revolvingLoan = await RevolvingLoanService.getRevolvingLoan() \n if ([LoanApprovalStatus.PENDING, LoanApprovalStatus.IN_PROGRESS, undefined].includes(revolvingLoan?.approvalStatus)) {\n throw new Error() \n }\n return revolvingLoan?.approvalStatus\n }, 6, 5000) \n decideNextStep()\n } catch (e) {\n console.error(e)\n decideNextStep()\n }\n }\n run()\n }, [])\n\n\n\n return (\n
\n

Verifying

\n Please wait while we verify your identity.
This usually takes less than 30 seconds.
\n
\n \n
\n \n \n
\n )\n}\n\n\nexport default PullingCredit","import React, { useEffect } from \"react\"\n\nimport classes from \"./AddGoals.module.scss\"\nimport { Analytics } from \"../../services/analytics\";\n\n\nexport const Reject: React.FC = () => {\n\n useEffect(() => {\n Analytics.pushEvent(\"app_apply_user_rejected\"); \n }, [])\n\n return (\n
\n

OPEN CREDIT CARD

\n
\n

WE'RE UNABLE TO PRE-QUALIFY YOU TODAY

\n
\n Thank you for applying for the Klutch Card. You should receive an email from us with more information regarding your application.\n
\n No hard credit inquiry was taken during this process.\n
\n\n
\n )\n}\n","/* eslint-disable */\n \n const loadVouched = (config) => {\n const existingScript = document.getElementById(\"vouched\");\n if (!existingScript) {\n const script = document.createElement(\"script\");\n script.src = \"https://static.vouched.id/widget/vouched-2.0.0.js\";\n script.id = \"vouched\";\n script.async = true;\n document.head.appendChild(script);\n script.onload = () => {\n var vouched = window[\"Vouched\"]({ ...config });\n vouched.mount(\"#vouched-element\");\n };\n }\n };\n \n export default loadVouched;","import classes from \"./LoadingAnimation.module.scss\";\n\ninterface LoadingAnimationProps {\n color?: string\n}\n\nexport default ({color}: LoadingAnimationProps) => {\n const c = color || \"#ffffff\"\n\n return (\n
\n
\n
\n
\n
\n
\n)\n}","/* eslint-disable */\n \nimport React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory, useLocation } from \"react-router-dom\"\nimport classes from \"./SecondaryInspection.module.scss\"\nimport loadVouched from \"./vouched-script\"\nimport LoadingAnimation from \"../../components/LoadingAnimation/LoadingAnimation\"\nimport { AccountService, User, UserService } from \"@klutch-card/klutch-js\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { Analytics } from \"../../services/analytics\"\n\n\nconst SecondaryInspection : React.FC = () => {\n\n const [user, setUser] = useState()\n const [initialized, setInitialized] = useState(false)\n const [failed, setFailed] = useState(false)\n\n const history = useHistory()\n\n useEffect(() => {\n Analytics.pushEvent(\"app_apply_secondary_verification\"); \n }, [])\n\n useEffect(() => {\n const run = async () => {\n const user = await UserService.getUser(true)\n setUser(user)\n }\n run()\n }, [])\n\n useEffect(() => {\n\n if (!user) {\n return\n }\n\n\n \n const config = {\n appId: \"cDSq-MxFm~#GMDorAp#7OCw.TRu1yM\", \n // optional verification information for comparison\n verification: {\n firstName: user.firstName,\n lastName: user.lastName,\n email: user.email,\n phone: user.phone,\n },\n \n // mobile handoff fields, a job will be created automatically if true\n crossDevice: true,\n crossDeviceQRCode: true,\n crossDeviceSMS: true,\n content: {\n review: \"Verification complete. Please return to your computer for next steps to complete your application\"\n },\n showTermsAndPrivacy: false, \n idLiveness: \"distance\",\n liveness: \"mouth\",\n includeBarcode: true,\n maxRetriesBeforeNext: 5,\n numForceRetries: 5,\n includeBackId: true,\n enableGeoLocation: true,\n face: \"camera\",\n id: \"camera\",\n // have the user confirm information\n userConfirmation: {\n confirmData: true,\n confirmImages: true,\n },\n \n // callback during initialization of the web app\n onInit: ({ token, job }: any) => {\n console.log(\"initialization\");\n setInitialized(true)\n },\n \n // callback when a user submits a photo\n onSubmit: ({ stage, attempts, job }: any) => { \n console.log(\"photo submitted\");\n },\n \n // called when a job is completed.\n onDone: async (job: any) => {\n // token used to query jobs\n await AccountService.addExtraData({vouched_job: job}) \n if (job.result.success) {\n console.log(\"scanning complete\", { token: job.token }); \n history.push(\"/apply/verifying\") \n } else {\n setFailed(true)\n }\n \n // An alternative way to update your system based on the\n // results of the job. Your backend could perform the following:\n // 1. query jobs with the token\n // 2. store relevant job information such as the id and\n // success property into the user's profile\n // fetch(`/yourapi/idv?job_token=${job.token}`);\n \n // Redirect to the next page based on the job success\n // if (job.result.success) {\n // window.location.replace(\"https://www.vouched.id/\");\n // } else {\n // window.location.replace(\"https://www.vouched.id/\");\n // }\n },\n \n // callback executed after attempt to find camera device\n onCamera: ({ hasCamera, hasPermission }: any) => {\n console.log(\"attempted to find camera\");\n },\n \n // callback when there are changes to the Camera DOM element\n onCameraEvent: (cameraEvent: any) => {\n console.log(\"camera DOM element updated\");\n },\n \n // callback when a reverification job is complete\n onReverify: (job: any) => {\n console.log(\"reverification complete\");\n },\n \n // callback when a survey is submitted, (per customer basis)\n onSurveyDone: (job: any) => {\n console.log(\"survey done\");\n },\n \n // callback when user confirms data and photos\n onConfirm: (userConfirmEvent: any) => {\n console.log(\"user confirmation\");\n },\n \n // theme\n theme: {\n name: \"avant\",\n },\n };\n\n loadVouched(config); \n }, [user?.id, failed]);\n\n if (failed) {\n return (\n
\n

Scan ID

\n
We could not validate your documents, please try again...
\n location.reload()}>NEXT\n
\n )\n }\n\n return (\n
\n { !initialized && (\n <>\n

Scan ID

\n
\n \n Loading Scanning Component...\n
\n \n )}\n
\n
\n
\n
\n )\n}\n\n\nexport default SecondaryInspection","import React, { useEffect } from \"react\"\n\n\nimport classes from \"./StateNotAvailable.module.scss\"\nimport { Analytics } from \"../../services/analytics\";\n\nexport const StateNotAvailable: React.FC = () => {\n\n useEffect(() => {\n Analytics.pushEvent(\"app_apply_state_not_available\"); \n }, [])\n \n\n return (\n
\n

KLUTCH IS NOT YET AVAILABLE IN YOUR STATE

\n
{`\n\n\nThank you for applying for the Klutch Card.\n\n\n\n\nUnfortunately, Klutch cannot currently serve new users residing in your state.\n\nWe are working hard to be able to serve you soon.\n\nWe will contact you with updates as soon as possible.\n\n\n\n\nNo credit inquiry was taken during this process.`}\n            
\n
\n )\n}\n\n\nexport default StateNotAvailable","import React, { FormEvent, useEffect, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\n\nimport classes from \"./AddGoals.module.scss\"\nimport { useCookies } from \"react-cookie\"\nimport { AuthService } from \"@klutch-card/klutch-js\"\nimport { Analytics } from \"../../services/analytics\"\n\n\n\n\n\nexport const ThankYou: React.FC = () => {\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n const history = useHistory()\n\n const [cookies, setCookie, removeCookie] = useCookies([\"provider\"]);\n\n const [token, setToken] = useState()\n\n\n useEffect(() => {\n Analytics.pushEvent(\"app_apply_manual_review\"); \n AuthService.addSubscriber(auth => {\n setToken(auth?.RefreshToken)\n })\n \n\n }, [])\n\n const handleFormSubmit = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n if (cookies[\"provider\"] == \"mobile\") {\n window.ReactNativeWebView.postMessage(JSON.stringify({\"action\": \"goto\", \"page\": \"/miniapps\", \"auth\": token}))\n } else {\n history.push(\"/miniapps\")\n }\n \n setLoading(false)\n }\n\n return (\n
\n

THANK YOU

\n
\n We will get back to you shortly.\n
\n Check back here or your email for updates. In the meantime, you can see the latest MiniApps.\n
\n\n
\n
{serverError} 
\n {/*CHECK OUT MINIAPPS */}\n
\n
\n )\n}\n","import React from \"react\";\nimport { useLocation } from \"react-router-dom\";\n\nexport function useQuery() {\n const { search } = useLocation();\n \n return React.useMemo(() => new URLSearchParams(search), [search]);\n }\n ","import { Account, AccountService, GraphQLException, UserService } from \"@klutch-card/klutch-js\"\nimport React, { ChangeEvent, FormEvent, useEffect, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\n\nimport classes from \"./VerifyPhone.module.scss\"\nimport { Analytics } from \"../../services/analytics\"\nimport { useQuery } from \"../../hooks/useQuery\"\nimport { Spinner } from \"react-activity\"\nimport { retry } from \"../../helper\"\n\nconst VerifyPhone: React.FC = () => {\n\n const query = useQuery()\n\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false) \n const [code, setCode] = useState(\"\")\n const [resendDisabled, setResendDisabled] = useState(false)\n const [resendTimer, setResendTimer] = useState(300)\n const [verifying, setVerifying] = useState(false)\n\n const phone = query.get(\"phone\") || \"\"\n\n const history = useHistory()\n\n const onVerifyPressed = async (e: FormEvent) => {\n e.preventDefault()\n setLoading(true)\n try {\n const user = await await UserService.changePhone(phone, code) \n if (user.phoneVerified) {\n Analytics.pushEvent(\"app_apply_phone_verified\"); \n setVerifying(true)\n // history.push(\"/apply/address\")\n return\n }\n } catch (e) { \n if (e instanceof GraphQLException) {\n const errorTypes = e.getAlloyCoreExceptionType() \n if (errorTypes && errorTypes.includes(\"com.alloycard.core.entities.user.PhoneVerificationException\")) {\n setServerError(\"The code you typed does not match the one we sent you. Click the button to receive another one\")\n } else if (errorTypes && errorTypes.includes(\"com.alloycard.core.entities.DuplicateEntityException\")) {\n setServerError(\"ALREADY_EXISTS\")\n } else {\n setServerError(e.message)\n } \n } else {\n setServerError((e as Error).message)\n } \n }\n setLoading(false)\n }\n\n useEffect(() => {\n if (!verifying) {\n return\n }\n\n (async () => {\n try {\n const hasAddress = await retry(async () => {\n console.log(\"retry attempt\") \n const account = await AccountService.getAccount()\n if (account.address) {\n return account.address\n }\n throw new Error() \n }, 6, 500) \n history.push(\"/apply/address\")\n } catch (e) {\n console.error(e)\n history.push(\"/apply/address\")\n } \n })()\n\n }, [verifying])\n\n const onResendPressed = async () => {\n setResendDisabled(true)\n await UserService.changePhone(phone)\n const i = setInterval(() => {\n setResendTimer(o => {\n const next = o - 1\n console.log(o)\n if (next < 0) {\n setResendDisabled(false)\n setResendTimer(300)\n clearInterval(i)\n }\n return next\n })\n }, 1000)\n }\n\n const getResendCaption = () => {\n if (!resendDisabled) {\n return \"RE-SEND CODE\"\n } \n const minutes = Math.floor(resendTimer / 60)\n let seconds = \"\" + (resendTimer % 60)\n if (seconds.length == 1) {\n seconds = \"0\" + seconds\n }\n return `${minutes}:${seconds}`\n } \n\n\n\n const changeCode = (e: ChangeEvent) => {\n const text = e.target.value\n const currentValue = text.replace(/[^\\d]/g, \"\")\n setCode(currentValue)\n }\n\n if (phone == null) {\n return null\n }\n\n if (verifying) {\n return (\n
\n

Verifying

\n Please wait while we verify your data.
This usually takes less than 5 seconds.
\n
\n \n
\n \n
\n )\n }\n\n return (\n
\n

Code

\n
\n {serverError == \"ALREADY_EXISTS\" ? (\n
There's already an account using this phone number.\n To login to the other account, please click on the link below: \n Log In with a different account\n
\n ) : (\n
{serverError} 
\n )}\n \n
{`Please enter the code sent to XXX-XXX-${phone.substring(phone.length - 4)}`}
\n \n
\n DIDN'T RECEIVE OUR SMS TEXT? \n {getResendCaption()} \n
\n
\n history.replace(\"/apply/phone\")} design=\"outline\" >BACK \n VERIFY \n
\n
\n
\n )\n}\n\n\nexport default VerifyPhone","import React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { KChangeEvent, KTextInput } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport classes from \"./Verifying.module.scss\"\nimport { AccountService, ApprovalStatus, UserService } from \"@klutch-card/klutch-js\"\nimport {DateTime} from \"luxon\"\nimport { Spinner } from \"react-activity\"\nimport \"react-activity/dist/library.css\";\nimport { retry } from \"../../helper\"\n\nconst Verifying : React.FC = () => {\n\n const history = useHistory()\n\n\n const decideNextStep = async () => {\n const account = await AccountService.getAccount()\n console.log(\"Acc :>> \", account.approvalStatus);\n switch (account.approvalStatus) {\n case ApprovalStatus.APPROVED: \n history.push(\"/apply/pulling-credit\")\n break;\n case ApprovalStatus.SECONDARY_INSPECTION: \n history.push(\"/apply/verify-document\")\n break;\n case ApprovalStatus.REJECTED: \n history.push(\"/apply/rejected\")\n break; \n case ApprovalStatus.CLOSED: \n history.push(\"/apply/rejected\")\n break; \n default:\n history.push(\"/apply/thankyou\")\n } \n }\n\n useEffect(() => {\n const run = async () => {\n try {\n\n await AccountService.requestApproval()\n const approvalStatus = await retry(async () => {\n const account = await AccountService.getAccount()\n if ([ApprovalStatus.REQUESTED, ApprovalStatus.NOT_VERIFIED, undefined].includes(account.approvalStatus)) {\n throw new Error()\n }\n return account.approvalStatus\n }, 6, 5000)\n await decideNextStep()\n } catch (e) { \n console.error(e) \n decideNextStep()\n }\n }\n run()\n },[])\n\n\n\n return (\n
\n

Verifying

\n Please wait while we verify your identity.
This usually takes less than 30 seconds.
\n
\n \n
\n \n \n
\n )\n}\n\n\nexport default Verifying","import { AccountService, UserService } from \"@klutch-card/klutch-js\"\nimport React, { FormEvent, useRef, useState, useCallback } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport {KTextInput, KChangeEvent } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\n\nimport classes from \"./UploadDocument.module.scss\"\nimport {useDropzone} from \"react-dropzone\"\n\n\n\nconst getBase64 = async (file: Blob): Promise => {\n const reader = new FileReader();\n reader.readAsDataURL(file as Blob);\n \n return new Promise((reslove, reject) => {\n reader.onload = () => reslove(reader.result as any);\n reader.onerror = (error) => reject(error);\n })\n }\n\n\nconst UploadDocument: React.FC = () => {\n\n const [serverError, setServerError] = useState(\"\")\n const [loading, setLoading] = useState(false)\n\n const [documentName, setDocumentName] = useState(\"\")\n const [documentDescription, setDocumentDescription] = useState(\"\")\n \n const [file, setFile] = useState()\n\n const history = useHistory()\n\n const inputs = [useRef(null)]\n\n const [uploaded, setUploaded] = useState(false)\n\n\n const onDrop = useCallback(async acceptedFiles => {\n console.log(acceptedFiles)\n setFile(acceptedFiles[0])\n }, [])\n const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})\n \n\n\n const handleSubmit = async (e: FormEvent) => { \n e.preventDefault()\n setLoading(true)\n const validated = inputs.map( i => {\n const v = i.current?.validate(true) \n if (v === undefined) {\n return true\n }\n return v == ValidationState.VALID\n }).reduce((prev, curr) => prev && curr)\n\n if (!validated) {\n setLoading(false)\n return\n }\n\n \n \n try {\n if (!file) {\n setServerError(\"Please select a file\")\n setLoading(false) \n return\n }\n const data = await getBase64(file!) as string\n const type = data.substring(5, data.indexOf(\";base64,\"))\n const base64 = data.substring(data.indexOf(\";base64,\") + 8)\n await AccountService.uploadDocument(documentName, documentDescription, type, base64)\n setUploaded(true)\n } catch (e) {\n setLoading(false)\n setServerError((e as Error).message)\n }\n \n }\n\n const reUpload = () => {\n setServerError(\"\")\n setFile(undefined)\n setDocumentName(\"\")\n setDocumentDescription(\"\")\n setUploaded(false)\n setLoading(false)\n }\n\n if (uploaded) {\n return (\n
\n

Upload Document

\n
\n

Thank you for uploading your document

\n

Our team is going to review it and get in touch with you

\n

If you want to upload another document, please press the button below.

\n UPLOAD ANOTHER DOCUMENT \n
\n
\n )\n }\n\n\n\n return (\n
\n

Upload Document

\n
\n
{serverError} 
\n

Use this form to securely upload documents to us.

\n setDocumentName(e.target.value)}\n validations={[\n {type: ValidationType.required, errorMessage: \"Name is a required field\"},\n {type: ValidationType.minLength, config: 3, errorMessage: \"Must contain at least 3 characters\"},\n {type: ValidationType.regex, config: /^([A-Z0-9a-z\\s])*$/g, errorMessage: \"Please enter a valid name\"}\n ]} \n /> \n setDocumentDescription(e.target.value)}\n /> \n {file ? (\n
\n File: {file?.name}\n
\n ) : ( \n
\n \n {\n isDragActive ?\n

Drop the files here ...

:\n

Drag 'n' drop some files here, or click to select files

\n }\n
\n )}\n UPLOAD \n \n
\n )\n}\n\n\nexport default UploadDocument","import React, { FormEvent, useEffect, useRef, useState } from \"react\"\nimport { useHistory } from \"react-router-dom\"\n\nimport classes from \"./ChoosePlan.module.scss\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport { LoanApprovalStatus, RevolvingLoanService, AccountService, ApprovalStatus } from \"@klutch-card/klutch-js\"\nimport LoadingAnimation from \"../../components/LoadingAnimation/LoadingAnimation\"\nimport { useAccount } from \"../../services/klutchHooks\"\n\nconst ChoosePlan: React.FC = () => {\n\n const [verifiedState, setVerifiedState] = useState()\n\n const history = useHistory() \n const account = useAccount()\n\n\n\n useEffect(() => {\n if (!account) {\n return\n }\n if (account.approvalStatus == ApprovalStatus.NOT_VERIFIED) {\n setVerifiedState(false)\n } \n if (account.approvalStatus == ApprovalStatus.APPROVED) {\n setVerifiedState(true)\n }\n\n }, [account])\n\n\n\n if (verifiedState === undefined) {\n return (<>)\n }\n\n const securedCardClicked = async () => {\n if (!verifiedState) {\n await AccountService.addExtraData({cardType: \"SECURED\"})\n history.replace(\"/apply\")\n \n } else {\n await RevolvingLoanService.createSecureLoan()\n history.replace(\"/apply/pulling-credit\") \n }\n }\n\n const unsecuredCardClicked = async () => {\n if (!verifiedState) {\n await AccountService.addExtraData({cardType: \"UNSECURED\"})\n history.replace(\"/apply\")\n }\n }\n\n const offers = () => {\n/* if (!accountProducts) {\n return ()\n }\n */ \n \n \n\n return (\n <>\n
\n
\n {/*

NOT ELIGIBLE

*/}\n
\n

Klutch Credit

\n
    \n
  • Unsecured Credit Card
  • \n
  • Up to 6% Cash back
  • \n
  • Free
  • \n
  • Multiple Virtual and physical cards
  • \n
\n \n {!verifiedState ? \"SELECT\" : \"NOT ELIGIBLE\"}\n \n
\n
\n

Klutch Lite

\n
    \n
  • Security Deposit Required
  • \n
  • Up to 6% Cash back
  • \n
  • Free
  • \n
  • Multiple Virtual and physical cards
  • \n
\n SELECT\n
\n \n )\n }\n\n return (\n
\n

Your Offers

\n

Please select what type of card you want:

\n {offers()}\n
\n )\n}\n\n\nexport default ChoosePlan","import * as React from \"react\";\nimport {KButton} from \"@klutchcard/klutch-webcomponents\";\nimport classes from \"./plaidbutton.module.scss\"\n\nimport {\n usePlaidLink,\n PlaidLinkOptions,\n PlaidLinkOnSuccess,\n PlaidLinkOnExitMetadata,\n PlaidLink,\n PlaidLinkError,\n } from \"react-plaid-link\";\nimport { TransferSource, TransferSourceService } from \"@klutch-card/klutch-js\";\nimport { useEffect } from \"react\";\n\n\nexport interface PlaidButtonProps {\n token: string\n onSuccess: (transferSource: TransferSource) => void,\n onError: (error: string) => void;\n}\n \nexport default ({token, onSuccess, onError}: PlaidButtonProps) => {\n\n const [loading, setLoading] = React.useState(true)\n\n const plaidSuccess = async (publicToken: string, metadata: any) => {\n \n console.log(\"Succcess\", {publicToken, metadata}) \n const resp = await TransferSourceService.addTransferSource(\n \"\",\n \"Plaid\",\n {\n publicToken,\n providerName: metadata.accounts[0].name,\n accountId: metadata.accounts[0].id\n }\n )\n onSuccess && onSuccess(resp)\n setLoading(false)\n }\n\n const plaidError = (err: PlaidLinkError | null, metadata: PlaidLinkOnExitMetadata) => {\n console.log(\"EXIT\", err)\n onError && onError(err?.display_message || \"\")\n setLoading(false)\n }\n \n\n const config: PlaidLinkOptions = {\n onSuccess: plaidSuccess,\n onExit: plaidError, \n onEvent: (eventName, metadata) => {undefined},\n token: token,\n //required for OAuth; if not using OAuth, set to null or omit:\n // receivedRedirectUri: window.location.href + \"?oauth_state_id=1234\",\n };\n const { open, exit, ready } = usePlaidLink(config);\n\n\n\n const openPlaid = () => {\n \n setLoading(true)\n open();\n }\n\n useEffect(() => {\n if (token) {\n setLoading(false)\n }\n }, [token])\n\n return ( \n CONNECT VIA PLAID\n )\n}","\nimport { useEffect, useState } from \"react\"\nimport classes from \"./SecurityDeposit.module.scss\"\nimport { CardPaymentService, Payment, TransferSource, TransferSourceService } from \"@klutch-card/klutch-js\"\n\nimport Plaidbutton from \"../../components/PlaidButton/plaidbutton\";\nimport { useHistory } from \"react-router-dom\";\nimport { toCurrency } from \"@klutchcard/klutch-webcomponents\";\n\nexport const SecurityDeposit: React.FC = () => {\n\n\n const [transferSource, setTransferSource] = useState()\n const [selectedOption, setSelectedOption] = useState<\"WIRE\" | \"PLAID\" | undefined>()\n const [linkToken, setLinkToken] = useState(\"\")\n const [payment, setPayment] = useState()\n \n\n const history = useHistory()\n\n useEffect(() => {\n (async () => {\n const payments = await CardPaymentService.listPayments()\n if (payments && payments.length > 0) {\n setPayment(payments[payments.length -1])\n } \n })()\n }, [])\n\n useEffect(() => { \n if (!selectedOption) {\n return\n }\n\n switch (selectedOption) {\n case \"WIRE\": \n (async () => {\n const transferSource = await TransferSourceService.addTransferSource(\"\", \"Inbound\", {})\n setTransferSource(transferSource)\n console.log(transferSource)\n })() ;\n break\n case \"PLAID\": \n (async () => {\n const linkToken = (await TransferSourceService.getTransferSourceConfig(\"Plaid\", {\n redirectUrl: window.location.href\n })).linkToken\n setLinkToken(linkToken)\n\n })() ;\n break\n }\n\n \n \n \n }, [selectedOption])\n\n \n\n\n const onPlaidSuccess = (ts: TransferSource) => { \n history.replace(\"/apply/security-deposit/plaid\", {transferSource: ts})\n }\n\n const onPlaidError = (err: string) => {\n console.log(\"Plaid Error\" , err)\n }\n\n return (\n
\n

Security Deposit

\n
\n

A security deposit is required to use your card.

\n

Please deposit an amount between $200 and $5,000.

\n

The deposit will be your card limit.

\n

You will receive your deposit back if you close your account or when converted to the Kluth Credit product.

\n

We will notify you via email when we receive your security deposit to finish setting up your account

\n
\n {payment ? (\n
\n We are processing your payment below. Please wait until the payment is fully settled to create your card.\n
\n Amount:${toCurrency(payment.amount)}\n Status:{payment.status}\n Funding Source:{payment.transferSource.name}\n
\n
\n ) : (\n
\n Choose how you want to send your security deposit:\n
setSelectedOption(\"WIRE\")}>\n
Wire (fastest)
\n Send us a wire from your bank. You can start using the card as soon as we receive the funds\n
\n
setSelectedOption(\"PLAID\")}>\n
Automatic Transfer
\n Connect your bank account via Plaid and we transfer the money. Approval in 1-2 weeks after we receive the funds\n
\n
\n )}\n {selectedOption == \"WIRE\" && (\n
\n

Wire Information

\n

We only accept wires for security deposits. We do not accept ACHs or checks

\n

Please use your name and address as receipient of the wire. Use the same info you used when signing up at Klutch.

\n
\n
\n

Routing Number: {transferSource?.routingNumber ?? \"Loading...\"}

\n

Account Number: {transferSource?.accountNumber ?? \"Loading...\"}

\n
\n
\n

Bank Address:

\n

Evolve Bank and Trust

\n

6070 Poplar Ave

\n

Suite 200

\n

Memphis, TN 38119

\n
\n
\n
\n )}\n {selectedOption == \"PLAID\" && linkToken && (\n
\n\n \n
\n )}\n
\n )\n}","import { FormEvent, useEffect, useRef, useState } from \"react\"\nimport classes from \"./PlaidSecurityDepositTransfer.module.scss\"\nimport {KTextInput, KChangeEvent } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\nimport { CardPaymentService, PaymentType, TransferSource, TransferSourceService, TransferSourceStatus } from \"@klutch-card/klutch-js\"\nimport { useHistory, useLocation, useParams } from \"react-router-dom\"\nimport { defer, filter, map, repeat, take } from \"rxjs\"\nimport LoadingAnimation from \"../../components/LoadingAnimation/LoadingAnimation\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\n\nexport const PlaidSecurityDepositTransfer: React.FC = () => {\n const [amount,setAmount] = useState(\"\")\n\n const [transferSource, setTransferSource] = useState()\n\n const location = useLocation<{transferSource: any}>()\n const [loading, setLoading] = useState(false)\n const input = useRef()\n const history = useHistory()\n const [paymentSent, setPaymentSent] = useState(false)\n const [serverError, setServerError] = useState(\"\")\n\n\n useEffect(() => {\n const source = defer(() => TransferSourceService.getTransferSources())\n \n const p = source.pipe(\n map(c => { \n return c.find(x => x.id == location.state?.transferSource.id)!\n }), \n repeat({count: 10, delay: 3000}),\n filter(c => c.status == TransferSourceStatus.ACTIVE),\n take(1) \n )\n p.subscribe((c: TransferSource) => { \n setTransferSource(c)\n })\n }, [])\n\n const changeAmount = (e: KChangeEvent) => {\n const value = e.target.value\n const currentValue = value.replace(/[^\\d]/g, \"\")\n\n setAmount(previousValue => {\n if (!previousValue || value.length > previousValue.length) {\n return currentValue.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n }\n return \"\" \n } )\n }\n\n const handleSubmit = async (e: FormEvent) => { \n e.preventDefault()\n const valid = input.current?.validate(true)\n if (valid != ValidationState.VALID) { \n return\n }\n setLoading(true) \n try {\n const am = amount.replace(/[^\\d]/g, \"\")\n const p = await CardPaymentService.createPayment(transferSource!.id, PaymentType.SECURITY_DEPOSIT, + am )\n setPaymentSent(true)\n } catch (e) {\n setServerError((e as Error).message) \n } \n setLoading(false)\n \n }\n\n/*export const CheckMark = ({ color, size = 10, ...props }: any) => (\n \n \n \n)\n*/\n\n const body = () => {\n if (paymentSent) {\n return (\n
\n

Your payment is on the way!

\n \n \n \n

We will send you an email when we received your payment and we are ready to issue your card.\n This might take up to 2 weeks. \n

\n
\n )\n }\n if (transferSource === undefined) {\n return (\n
\n Loading your bank information\n \n
)\n }\n \n if (transferSource === null || transferSource.maxTransferAmount < 200 || transferSource.riskIndex >= 80) {\n return (\n
\n

Your bank account is ineligble to be used as a funding source for your security deposit.

\n

Please use our \"Wire\" option to submit a deposit.

\n history.replace(\"/apply/security-deposit\")}>USE WIRE\n
\n )\n }\n\n const maxPayment = Math.min(transferSource.maxTransferAmount, 5000)\n \n return (\n <>\n

Please transfer an amount between 200 and {maxPayment}

\n
\n Pay from: {transferSource.name}\n
\n\n
\n \n SEND PAYMENT\n By clicking \"SEND PAYMENT\", you are authorizing us to initiate a debit on your behalf in the details listed above\n \n \n )\n }\n\n return (\n
\n

Security Deposit

\n
\n {serverError} \n {body()}\n
\n\n
\n )\n\n}\n\n","import { AccountService, UserService } from \"@klutch-card/klutch-js\"\nimport React, { FormEvent, useRef, useState, useCallback } from \"react\"\nimport { useHistory } from \"react-router-dom\"\nimport {KButton} from \"@klutchcard/klutch-webcomponents\"\nimport {KTextInput, KChangeEvent, Select, Dialog } from \"@klutchcard/klutch-webcomponents\"\nimport { ValidationState, ValidationType } from \"@klutchcard/klutch-webcomponents\"\n\nimport classes from \"./AdditionalVerificationDocuments.module.scss\"\nimport {useDropzone} from \"react-dropzone\"\n\n\n\n\nconst getBase64 = async (file: Blob): Promise => {\n const reader = new FileReader();\n reader.readAsDataURL(file as Blob);\n \n return new Promise((reslove, reject) => {\n reader.onload = () => reslove(reader.result as any);\n reader.onerror = (error) => reject(error);\n })\n }\n\n\nconst documents = [\n {label: \"Drivers License\", value:\"driverslicense\"},\n {label: \"Passport\", value: \"passport\"},\n {label: \"State ID\", value: \"stateID\"},\n {label: \"SSN Card\", value: \"socialSecurityNumber\"},\n {label: \"Utility Bill\", value: \"utilityBill\"},\n {label: \"Phone Bill\", value: \"phonebill\"},\n {label: \"Proof of Income\", value: \"incomeProof\"},\n {label: \"Pay stub\", value: \"paystub\"},\n {label: \"Proof of Address\", value: \"addressProof\"},\n {label: \"Other Document\", value: \"other\"},\n]\n\nconst AdditionalVerificationDocuments: React.FC = () => {\n\n\n const [linkedIn, setLinkedIn] = useState(\"\")\n const [twitter, setTwitter] = useState(\"\")\n const [instagram, setInstagram] = useState(\"\")\n const [github, setGithub] = useState(\"\")\n const [tiktok, setTikTok] = useState(\"\")\n\n const [documentType, setDocumentType] = useState(\"\")\n const [file, setFile] = useState()\n\n\n const onDrop = useCallback(async acceptedFiles => {\n console.log(acceptedFiles)\n setFile(acceptedFiles[0])\n }, [])\n const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})\n\n return (\n
\n

Additional Verification

\n
\n\n

Our system uses a variety of information sources to make a decision. \n As with many automated system, ours is prone to errors too. \n All of those are optional, but they can help us verify your identity and approve you for the card.

\n
\n DOCUMENTS (OPTIONAL):\n ADD DOCUMENT\n\n SOCIAL MEDIA (OPTIONAL):\n CONNECT SOCIAL MEDIA \n
\n
\n console.log(\"SD\"))},\n {label: \"UPLOAD\", type: \"primary\", onClick: (()=> console.log(\"SD\"))} \n ]}\n show\n title=\"Add Document\"\n onDismiss={() => console.log(\"DISMISS\")}\n >\n
\n \n {op.label}}\n arrowColor=\"white\"\n value={documentType}\n onValueChanged={e => setDocumentType(e.value)}\n > \n
\n \n {\n isDragActive ?\n

Drop the files here ...

:\n

Drag 'n' drop some files here, or click to select files

\n }\n
\n
\n \n
\n )\n}\n\n\nexport default AdditionalVerificationDocuments","import { Redirect, Route, Switch } from \"react-router-dom\"\nimport AddAddress, { availableStates } from \"./AddAddress\"\nimport AddAnnualIncome from \"./AddAnnualIncome\"\nimport { AddGoals } from \"./AddGoals\"\nimport AddPhone from \"./AddPhone\"\nimport AddSSN from \"./AddSSN\"\nimport { CreditFrozen } from \"./CreditFrozen\"\nimport { DownloadApp } from \"./DownloadApp\"\nimport { PreApproved } from \"./PreApproved\"\nimport PullingCredit from \"./PullingCredit\"\nimport { Reject } from \"./Reject\"\nimport SecondaryInspection from \"./SecondaryInspection\"\nimport {StateNotAvailable} from \"./StateNotAvaliable\"\nimport { ThankYou } from \"./ThankYou\"\nimport VerifyPhone from \"./VerifyPhone\"\nimport Verifying from \"./Veryfing\"\nimport classes from \"./index.module.scss\"\nimport { useAccount, useUser } from \"../../services/klutchHooks\"\nimport { AccountService, ApprovalStatus, LoanApprovalStatus, RevolvingLoan, RevolvingLoanService, RevolvingLoanType } from \"@klutch-card/klutch-js\"\nimport App from \"../../App\"\nimport UploadDocument from \"./UploadDocument\"\nimport { useEffect, useState } from \"react\"\nimport ChoosePlan from \"./ChoosePlan\"\nimport { SecurityDeposit } from \"./SecurityDeposit\"\nimport { PlaidSecurityDepositTransfer } from \"./PlaidSecurityDepositTransfer\"\nimport AdditionalVerificationDocuments from \"./AdditionalVerificationDocuments\"\n\nexport const ApplyRouter = (): JSX.Element => (\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n)\n\n\n\nconst ApplyDecide = (): JSX.Element => {\n const account = useAccount()\n const user = useUser()\n\n const [revolvingLoan, setRevolvingLoan] = useState()\n \n useEffect(() => {\n const run = async () => {\n const rl = await RevolvingLoanService.getRevolvingLoan()\n setRevolvingLoan(rl)\n }\n run()\n }, [])\n \n\n if (!account || !user) {\n return (\n
Loading...
\n )\n }\n if (revolvingLoan === undefined) {\n return (\n
Loading...
\n )\n }\n \n\n console.log(\"STATUS: \", { account, revolvingLoan, user})\n\n\n if (!account || !user) {\n return (\n \n )\n }\n\n\n if (account.approvalStatus == ApprovalStatus.SECONDARY_INSPECTION) {\n return (\n \n )\n }\n \n\n if (account.approvalStatus == ApprovalStatus.REJECTED || account.approvalStatus == ApprovalStatus.CLOSED) {\n return (\n \n )\n }\n \n \n if (!user.phone || !user.phoneVerified) {\n return (\n \n )\n }\n\n if (!user.birthDate) {\n return (\n \n )\n }\n\n if (!account.address) {\n return (\n \n )\n }\n if (!availableStates.includes(account.address.state.toUpperCase())) {\n return (\n \n )\n }\n\n if (!user.hasSSN) {\n return (\n \n )\n } \n\n if (account.approvalStatus == ApprovalStatus.NOT_VERIFIED) {\n return ()\n }\n\n if (account.approvalStatus == ApprovalStatus.REQUESTED) {\n return ()\n }\n\n if (account.approvalStatus == ApprovalStatus.MANUAL_REVIEW) {\n return ()\n }\n\n \n if (account.approvalStatus == ApprovalStatus.WAITING_DOCS) {\n return ()\n }\n\n\n if (account.approvalStatus == ApprovalStatus.APPROVED) {\n\n\n if (!revolvingLoan) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.REJECTED) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.IN_PROGRESS) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.APPROVED) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.MANUAL_REVIEW) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.PENDING) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.REPORT_FROZEN) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.WAITING_AGREEMENT) {\n return ()\n } \n if (revolvingLoan.approvalStatus == LoanApprovalStatus.WAITING_DEPOSIT) {\n return ()\n }\n if (revolvingLoan.approvalStatus == LoanApprovalStatus.APPROVED) {\n return ()\n }\n }\n\n return (\n \n )\n \n \n}\n\nexport default ApplyRouter\n","import { Route, Switch } from \"react-router-dom\"\nimport classes from \"./NavBody.module.scss\"\nimport { PropsWithChildren, useEffect } from \"react\"\nimport { RevolvingLoanService } from \"@klutch-card/klutch-js\"\n\n\nexport interface NavBodyProperties {\n title: string\n className?: string\n}\n\nexport const NavBody: React.FC> = ({title, children, className}) => {\n\n\n return (\n
\n

{title}

\n
\n {children}\n
\n
\n )\n}\n\n\nexport default NavBody","import { Entity, Recipe, RecipeInstall, RecipePanel, RecipesService } from \"@klutch-card/klutch-js\"\nimport { useEffect, useState } from \"react\"\n\nlet allrecipes: Recipe[] = []\n\nexport default function useRecipes(callback?: (recipes: Recipe[]) => void) {\n const [recipes, setRecipes] = useState(allrecipes)\n\n\n useEffect(() => {\n const subscription = RecipesService.addRecipesSubscriber((r: Recipe[]) => {\n allrecipes = r\n setRecipes(r)\n callback && callback(r)\n })\n return () => subscription.unsubscribe()\n }, [recipes]) \n return recipes\n}\n\nexport function useInstalledRecipes(callback?: (recipes: RecipeInstall[]) => void) {\n const [recipes, setRecipes] = useState(null)\n\n useEffect(() => {\n const subscription = RecipesService.addRecipeInstallSubscriber((r: RecipeInstall[]) => {\n setRecipes(r)\n callback && callback(r)\n })\n return () => subscription.unsubscribe()\n }, []) \n return recipes\n}\n\nexport function useRecipe(recipeId: string): [Recipe | null, () => void] {\n const [recipe, setRecipe] = useState(null)\n const updateRecipe = () => {\n RecipesService.getRecipe(recipeId).then(r => {\n setRecipe(r)\n })\n }\n useEffect(() => {\n updateRecipe()\n }, [recipeId])\n return ([recipe, updateRecipe])\n}\n\n\nexport function usePanels(entity: Entity | undefined): RecipePanel[] | null {\n const [panels, setPanels] = useState(null)\n useEffect(() => {\n const sub = RecipesService.addRecipePanelsSubscriber(entity, setPanels)\n return () => sub.unsubscribe()\n }, [entity?.entityID])\n return panels\n}\n\nexport function useRecipeInstall(recipeId: string): RecipeInstall | undefined {\n const [recipeInstall, setRecipeInstall] = useState()\n useEffect(() => {\n (async () => {\n const recipeInstall = await RecipesService.findRecipeInstallByRecipe(recipeId)\n setRecipeInstall(recipeInstall)\n })()\n }, [recipeId])\n return recipeInstall\n}","import { RecipePanel, RecipePanelSize, RecipesService } from \"@klutch-card/klutch-js\"\nimport { useHistory, useLocation } from \"react-router-dom\"\nimport classes from \"./MiniAppDynamicComponent.module.scss\"\nimport { LegacyRef, useEffect, useRef, useState } from \"react\"\n\nexport interface MiniAppDynamicComponentProps {\n panel: RecipePanel\n}\n\ntype KlutchMessage = {\n action: string, \n params?: any\n}\n\n\n\nexport const MiniAppDynamicComponent = ({panel}: MiniAppDynamicComponentProps): JSX.Element => {\n \n const history = useHistory()\n const location = useLocation()\n const frame = useRef()\n \n if (!panel || !panel.recipeInstall || !panel.recipeInstall.recipe) {\n return <>\n }\n\n const recipeInstall = panel.recipeInstall\n const recipe = panel.recipeInstall.recipe\n \n\n\n\n\n let templateUrl = recipe.templates?.find(c => c != null && c.name == panel.templateFileName.substring(1) || c.name == panel.templateFileName)?.url \n\n \n if (templateUrl) {\n if (templateUrl.indexOf(\"?\") >= 0) {\n templateUrl = templateUrl + location.search.replace(\"?\", \"&\")\n } else {\n templateUrl = templateUrl + location.search \n } \n }\n\n\n \n\n \n\n const [token, setToken] = useState(\"\")\n \n useEffect(()=> {\n const runOnce = async () => {\n const token = await RecipesService.getRecipeInstallToken(recipeInstall.id)\n setToken(token)\n }\n runOnce()\n }, [])\n\n\n\n\n\n useEffect(() => { \n if (!frame || !templateUrl || !token) {\n return\n }\n\n let port: MessagePort\n\n const frameLoadHandler = (e: any) => {\n const channel = new MessageChannel()\n port = channel.port1\n port.onmessage = messageHandler\n frame.current?.contentWindow?.postMessage(\"KlutchInit\", templateUrl, [channel.port2]) \n \n }\n\n frame.current?.addEventListener(\"load\", frameLoadHandler)\n\n \n\n\n const messageHandler = (event: MessageEvent) => {\n const obj = JSON.parse(event.data) as KlutchMessage \n switch (obj.action) {\n case \"ready\": {\n port.postMessage({\n templateData: panel.data, \n token: token, \n recipeInstallConfig: recipeInstall.configuration,\n recipeInstallId: recipeInstall.id,\n locationState: location.state\n }) \n break;\n }\n case \"openTemplate\": {\n const {name, replace, state} = obj.params \n const location = `/miniapps/${recipe.id}/${name}`.replaceAll(\"//\", \"/\")\n if (replace) {\n history.replace(location, state)\n } else {\n history.push(location, state)\n }\n break;\n } \n case \"changePanelData\": { \n const {data} = obj.params\n RecipesService.changePanelData(panel, data)\n break;\n } \n case \"changeRecipeInstallConfiguration\": {\n const {data} = obj.params\n RecipesService.changeRecipeInstallConfig(recipeInstall.id, data)\n break;\n }\n case \"goto\": { \n const {location, replace, state} = obj.params\n if (replace) {\n history.replace(location, state)\n } else {\n history.push(location, state)\n }\n break;\n }\n case \"goBack\": {\n history.goBack()\n break;\n } \n case \"configPanel\": { \n //props.panelConfigCallback && props.panelConfigCallback(obj.params)\n break;\n } \n case \"addPanel\" : {\n const {templateName, data, entity, order, size} = obj.params\n RecipesService.addPanel(recipeInstall.id, templateName, data, entity, order, size)\n break;\n }\n case \"openExternalUrl\": {\n const {url} = obj.params\n window.open(url)\n }\n }\n }\n\n //window.addEventListener(\"message\", messageHandler) \n\n \n\n return () => {\n\n frame.current?.removeEventListener(\"load\", frameLoadHandler)\n }\n }, [frame, token])\n\n const drawBody = () => {\n if (/* recipe.version < 2 || */ !templateUrl) {\n return (\n
This miniapp is only available on the Klutch App for now
\n )\n } else {\n if (!token) {\n return (<>)\n }\n return (\n \n )\n }\n }\n \n return (\n
\n

{panel?.recipeInstall.recipe?.name}

\n
\n {drawBody()} \n
\n
\n )\n} ","import classes from \"./MiniAppSkeleton.module.scss\"\n\nexport const MiniAppPanelSkeleton: React.FC = () => {\n return (\n
\n
\n
\n \n
\n
\n
\n
\n )\n}","import { UserMessage, UserService } from \"@klutch-card/klutch-js\"\nimport { Dialog, Modal } from \"@klutchcard/klutch-webcomponents\"\nimport { useEffect, useState } from \"react\"\nimport classes from \"./PopupView.module.scss\"\n\nexport const PopupView: React.FC = () => {\n\n const [message, setMessage] = useState()\n\n \n useEffect(() => {\n (async () => {\n const messages = await UserService.getUserMessages() \n if (messages?.length > 0) {\n const m = messages.filter(c => !c.isRead)\n console.log(\"M\", m)\n if (m) {\n setMessage(m[0])\n }\n \n }\n \n })()\n \n }, [])\n\n if (!message) {\n return null\n }\n\n const closeClicked = async () => {\n if (message) {\n await UserService.markUserMessageAsRead(message.id) \n setMessage(undefined)\n }\n }\n\n return (\n \n
\n
\n

{message.body}

\n close\n
\n