feat(webapp): popup Plaid Link component

This commit is contained in:
Ahmed Bouhuolia
2024-01-30 23:09:29 +02:00
parent b9886cfac3
commit b43cd26ecc
7 changed files with 540 additions and 4 deletions

View File

@@ -63,4 +63,33 @@ GOTENBERG_DOCS_URL=http://server:3000/public/
EXCHANGE_RATE_SERVICE=open-exchange-rate
# Open Exchange Rate
OPEN_EXCHANGE_RATE_APP_ID=
OPEN_EXCHANGE_RATE_APP_ID=
# The Plaid environment to use ('sandbox' or 'development').
# https://plaid.com/docs/#api-host
PLAID_ENV=sandbox
# Your Plaid keys, which can be found in the Plaid Dashboard.
# https://dashboard.plaid.com/account/keys
PLAID_CLIENT_ID=
PLAID_SECRET_DEVELOPMENT=
PLAID_SECRET_SANDBOX=
# (Optional) Redirect URI settings section
# Only required for OAuth redirect URI testing (not common on desktop):
# Sandbox Mode:
# Set the PLAID_SANDBOX_REDIRECT_URI below to 'http://localhost:3001/oauth-link'.
# The OAuth redirect flow requires an endpoint on the developer's website
# that the bank website should redirect to. You will also need to configure
# this redirect URI for your client ID through the Plaid developer dashboard
# at https://dashboard.plaid.com/team/api.
# Development mode:
# When running in development mode, you must use an https:// url.
# You will need to configure this https:// redirect URI in the Plaid developer dashboard.
# Instructions to create a self-signed certificate for localhost can be found at
# https://github.com/plaid/pattern/blob/master/README.md#testing-oauth.
# If your system is not set up to run localhost with https://, you will be unable to test
# the OAuth in development and should leave the PLAID_DEVELOPMENT_REDIRECT_URI blank.
PLAID_SANDBOX_REDIRECT_URI=
PLAID_DEVELOPMENT_REDIRECT_URI=

View File

@@ -69,6 +69,9 @@
"moment-timezone": "^0.5.33",
"path-browserify": "^1.0.1",
"prop-types": "15.8.1",
"plaid": "^9.3.0",
"plaid-threads": "^11.4.3",
"react-plaid-link": "^3.2.1",
"query-string": "^7.1.1",
"ramda": "^0.27.1",
"react": "^18.2.0",

View File

@@ -0,0 +1,121 @@
import React, { useEffect } from 'react';
import {
usePlaidLink,
PlaidLinkOnSuccessMetadata,
PlaidLinkOnExitMetadata,
PlaidLinkError,
PlaidLinkOptionsWithLinkToken,
PlaidLinkOnEventMetadata,
PlaidLinkStableEvent,
} from 'react-plaid-link';
import { useHistory } from 'react-router-dom';
// import { exchangeToken, setItemState } from '../services/api';
// import { useItems, useLink, useErrors } from '../services';
interface LaunchLinkProps {
isOauth?: boolean;
token: string;
userId: number;
itemId?: number | null;
children?: React.ReactNode;
}
// Uses the usePlaidLink hook to manage the Plaid Link creation. See https://github.com/plaid/react-plaid-link for full usage instructions.
// The link token passed to usePlaidLink cannot be null. It must be generated outside of this component. In this sample app, the link token
// is generated in the link context in client/src/services/link.js.
export function LaunchLink(props: LaunchLinkProps) {
const history = useHistory();
// const { getItemsByUser, getItemById } = useItems();
// const { generateLinkToken, deleteLinkToken } = useLink();
// const { setError, resetError } = useErrors();
// define onSuccess, onExit and onEvent functions as configs for Plaid Link creation
const onSuccess = async (
publicToken: string,
metadata: PlaidLinkOnSuccessMetadata,
) => {
// log and save metatdata
// logSuccess(metadata, props.userId);
if (props.itemId != null) {
// update mode: no need to exchange public token
// await setItemState(props.itemId, 'good');
// deleteLinkToken(null, props.itemId);
// getItemById(props.itemId, true);
// regular link mode: exchange public token for access token
} else {
// call to Plaid api endpoint: /item/public_token/exchange in order to obtain access_token which is then stored with the created item
// await exchangeToken(
// publicToken,
// metadata.institution,
// metadata.accounts,
// props.userId,
// );
// getItemsByUser(props.userId, true);
}
// resetError();
// deleteLinkToken(props.userId, null);
history.push(`/user/${props.userId}`);
};
const onExit = async (
error: PlaidLinkError | null,
metadata: PlaidLinkOnExitMetadata,
) => {
// log and save error and metatdata
// logExit(error, metadata, props.userId);
if (error != null && error.error_code === 'INVALID_LINK_TOKEN') {
// await generateLinkToken(props.userId, props.itemId);
}
if (error != null) {
// setError(error.error_code, error.display_message || error.error_message);
}
// to handle other error codes, see https://plaid.com/docs/errors/
};
const onEvent = async (
eventName: PlaidLinkStableEvent | string,
metadata: PlaidLinkOnEventMetadata,
) => {
// handle errors in the event end-user does not exit with onExit function error enabled.
if (eventName === 'ERROR' && metadata.error_code != null) {
// setError(metadata.error_code, ' ');
}
// logEvent(eventName, metadata);
};
const config: PlaidLinkOptionsWithLinkToken = {
onSuccess,
onExit,
onEvent,
token: props.token,
};
if (props.isOauth) {
// add additional receivedRedirectUri config when handling an OAuth reidrect
config.receivedRedirectUri = window.location.href;
}
const { open, ready } = usePlaidLink(config);
useEffect(() => {
// initiallizes Link automatically
if (props.isOauth && ready) {
open();
} else if (ready) {
// regular, non-OAuth case:
// set link token, userId and itemId in local storage for use if needed later by OAuth
localStorage.setItem(
'oauthConfig',
JSON.stringify({
userId: props.userId,
itemId: props.itemId,
token: props.token,
}),
);
open();
}
}, [ready, open, props.isOauth, props.userId, props.itemId, props.token]);
return <></>;
}

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
import React from 'react';
import React, { useState } from 'react';
import {
Button,
NavbarGroup,
@@ -14,7 +14,10 @@ import {
Icon,
FormattedMessage as T,
} from '@/components';
import { useRefreshCashflowAccounts } from '@/hooks/query';
import {
useGetPlaidLinkToken,
useRefreshCashflowAccounts,
} from '@/hooks/query';
import { CashflowAction, AbilitySubject } from '@/constants/abilityOption';
import withDialogActions from '@/containers/Dialog/withDialogActions';
@@ -26,6 +29,7 @@ import { ACCOUNT_TYPE } from '@/constants';
import { DialogsName } from '@/constants/dialogs';
import { compose } from '@/utils';
import { LaunchLink } from '@/containers/Banking/Plaid/PlaidLanchLink';
/**
* Cash Flow accounts actions bar.
@@ -63,8 +67,20 @@ function CashFlowAccountsActionsBar({
setCashflowAccountsTableState({ inactiveMode: checked });
};
const { mutateAsync: getPlaidLinkToken } = useGetPlaidLinkToken();
const [linkToken, setLinkToken] = useState<string>('');
const handleConnectToBank = () => {
getPlaidLinkToken()
.then((res) => {
setLinkToken(res.data.link_token);
})
.catch(() => {});
};
return (
<DashboardActionsBar>
<LaunchLink userId={3} token={linkToken} />
<NavbarGroup>
<Can I={CashflowAction.Create} a={AbilitySubject.Cashflow}>
<Button
@@ -104,6 +120,12 @@ function CashFlowAccountsActionsBar({
onChange={handleInactiveSwitchChange}
/>
</Can>
<Button
className={Classes.MINIMAL}
text={'Connect to Bank'}
onClick={handleConnectToBank}
/>
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>

View File

@@ -37,3 +37,4 @@ export * from './transactionsLocking';
export * from './warehouses';
export * from './branches';
export * from './warehousesTransfers';
export * from './plaid';

View File

@@ -0,0 +1,17 @@
// @ts-nocheck
import { useMutation } from 'react-query';
import useApiRequest from '../useRequest';
/**
* Retrieves the plaid link token.
*/
export function useGetPlaidLinkToken(props) {
const apiRequest = useApiRequest();
return useMutation(
() => apiRequest.post('banking/plaid/link-token', {}, {}),
{
...props,
},
);
}

345
pnpm-lock.yaml generated
View File

@@ -236,6 +236,9 @@ importers:
objection-unique:
specifier: ^1.2.2
version: 1.2.2(objection@3.0.1)
plaid:
specifier: ^10.3.0
version: 10.9.0
pluralize:
specifier: ^8.0.0
version: 8.0.0
@@ -615,6 +618,12 @@ importers:
path-browserify:
specifier: ^1.0.1
version: 1.0.1
plaid:
specifier: ^9.3.0
version: 9.12.0
plaid-threads:
specifier: ^11.4.3
version: 11.5.0(react-dom@18.2.0)(react@18.2.0)
prop-types:
specifier: 15.8.1
version: 15.8.1
@@ -660,6 +669,9 @@ importers:
react-loadable:
specifier: ^5.5.0
version: 5.5.0(react@18.2.0)
react-plaid-link:
specifier: ^3.2.1
version: 3.5.1(react-dom@18.2.0)(react@18.2.0)
react-query:
specifier: ^3.6.0
version: 3.39.3(react-dom@18.2.0)(react@18.2.0)
@@ -3929,16 +3941,69 @@ packages:
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
engines: {node: '>=10.0.0'}
/@emotion/cache@10.0.29:
resolution: {integrity: sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==}
dependencies:
'@emotion/sheet': 0.9.4
'@emotion/stylis': 0.8.5
'@emotion/utils': 0.11.3
'@emotion/weak-memoize': 0.2.5
dev: false
/@emotion/core@10.3.1(react@18.2.0):
resolution: {integrity: sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==}
peerDependencies:
react: '>=16.3.0'
dependencies:
'@babel/runtime': 7.20.13
'@emotion/cache': 10.0.29
'@emotion/css': 10.0.27
'@emotion/serialize': 0.11.16
'@emotion/sheet': 0.9.4
'@emotion/utils': 0.11.3
react: 18.2.0
dev: false
/@emotion/css@10.0.27:
resolution: {integrity: sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==}
dependencies:
'@emotion/serialize': 0.11.16
'@emotion/utils': 0.11.3
babel-plugin-emotion: 10.2.2
dev: false
/@emotion/hash@0.8.0:
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
dev: false
/@emotion/is-prop-valid@1.2.0:
resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==}
dependencies:
'@emotion/memoize': 0.8.0
dev: false
/@emotion/memoize@0.7.4:
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
dev: false
/@emotion/memoize@0.8.0:
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
dev: false
/@emotion/serialize@0.11.16:
resolution: {integrity: sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==}
dependencies:
'@emotion/hash': 0.8.0
'@emotion/memoize': 0.7.4
'@emotion/unitless': 0.7.5
'@emotion/utils': 0.11.3
csstype: 2.6.21
dev: false
/@emotion/sheet@0.9.4:
resolution: {integrity: sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==}
dev: false
/@emotion/stylis@0.8.5:
resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==}
dev: false
@@ -3947,6 +4012,14 @@ packages:
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
dev: false
/@emotion/utils@0.11.3:
resolution: {integrity: sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==}
dev: false
/@emotion/weak-memoize@0.2.5:
resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==}
dev: false
/@eslint-community/eslint-utils@4.4.0(eslint@8.33.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -7529,6 +7602,12 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
/add-dom-event-listener@1.1.0:
resolution: {integrity: sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==}
dependencies:
object-assign: 4.1.1
dev: false
/add-stream@1.0.0:
resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==}
dev: true
@@ -8161,6 +8240,14 @@ packages:
engines: {node: '>=4'}
dev: false
/axios@0.21.4:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies:
follow-redirects: 1.15.2
transitivePeerDependencies:
- debug
dev: false
/axios@1.6.0:
resolution: {integrity: sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==}
dependencies:
@@ -8241,6 +8328,21 @@ packages:
webpack: 5.76.0(webpack-cli@4.10.0)
dev: false
/babel-plugin-emotion@10.2.2:
resolution: {integrity: sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==}
dependencies:
'@babel/helper-module-imports': 7.22.15
'@emotion/hash': 0.8.0
'@emotion/memoize': 0.7.4
'@emotion/serialize': 0.11.16
babel-plugin-macros: 2.8.0
babel-plugin-syntax-jsx: 6.18.0
convert-source-map: 1.9.0
escape-string-regexp: 1.0.5
find-root: 1.1.0
source-map: 0.5.7
dev: false
/babel-plugin-istanbul@5.2.0:
resolution: {integrity: sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==}
engines: {node: '>=6'}
@@ -8283,6 +8385,14 @@ packages:
'@types/babel__traverse': 7.18.3
dev: false
/babel-plugin-macros@2.8.0:
resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==}
dependencies:
'@babel/runtime': 7.20.13
cosmiconfig: 6.0.0
resolve: 1.22.6
dev: false
/babel-plugin-macros@3.1.0:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
@@ -8459,6 +8569,13 @@ packages:
- supports-color
dev: false
/babel-runtime@6.26.0:
resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==}
dependencies:
core-js: 2.6.12
regenerator-runtime: 0.11.1
dev: false
/babel-walk@3.0.0-canary-5:
resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==}
engines: {node: '>= 10.0.0'}
@@ -9684,9 +9801,19 @@ packages:
resolution: {integrity: sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==}
dev: false
/component-classes@1.2.6:
resolution: {integrity: sha512-hPFGULxdwugu1QWW3SvVOCUHLzO34+a2J6Wqy0c5ASQkfi9/8nZcBB0ZohaEbXOQlCflMAEMmEWk7u7BVs4koA==}
dependencies:
component-indexof: 0.0.3
dev: false
/component-emitter@1.3.0:
resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
/component-indexof@0.0.3:
resolution: {integrity: sha512-puDQKvx/64HZXb4hBwIcvQLaLgux8o1CbWl39s41hrIIZDl1lJiD5jc22gj3RBeGK0ovxALDYpIbyjqDUUl0rw==}
dev: false
/compressible@2.0.18:
resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
engines: {node: '>= 0.6'}
@@ -9958,6 +10085,12 @@ packages:
requiresBuild: true
dev: false
/core-js@2.6.12:
resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==}
deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
requiresBuild: true
dev: false
/core-js@3.27.2:
resolution: {integrity: sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w==}
requiresBuild: true
@@ -10216,6 +10349,13 @@ packages:
uid-safe: 2.1.5
dev: false
/css-animation@1.6.1:
resolution: {integrity: sha512-/48+/BaEaHRY6kNQ2OIPzKf9A6g8WjZYjhiNDNuIVbsm5tXCGIAsHDjB4Xu1C4vXJtUWZo26O68OQkDpNBaPog==}
dependencies:
babel-runtime: 6.26.0
component-classes: 1.2.6
dev: false
/css-blank-pseudo@3.0.3(postcss@8.4.21):
resolution: {integrity: sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==}
engines: {node: ^12 || ^14 || >=16}
@@ -10480,6 +10620,10 @@ packages:
cssom: 0.3.8
dev: false
/csstype@2.6.21:
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
dev: false
/csstype@3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
dev: false
@@ -10999,6 +11143,10 @@ packages:
resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
dev: false
/dom-align@1.12.4:
resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==}
dev: false
/dom-converter@0.2.0:
resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==}
dependencies:
@@ -12167,6 +12315,10 @@ packages:
signal-exit: 3.0.7
strip-final-newline: 2.0.0
/exenv@1.2.2:
resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==}
dev: false
/exit@0.1.2:
resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
engines: {node: '>= 0.8.0'}
@@ -12603,7 +12755,6 @@ packages:
/find-root@1.1.0:
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
dev: true
/find-up@1.1.2:
resolution: {integrity: sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==}
@@ -17165,6 +17316,10 @@ packages:
fs-monkey: 1.0.5
dev: false
/memoize-one@5.2.1:
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
dev: false
/memory-cache@0.2.0:
resolution: {integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==}
dev: false
@@ -19165,6 +19320,42 @@ packages:
find-up: 3.0.0
dev: false
/plaid-threads@11.5.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-KS3w5Ydv+aC7wS1XxiWeDUhQHHFQ/5dQOoAQwdg+3DxFHbh5nSPH1L4Zy9qHru5FVDmC5DQ6/9XhJwzZ9BhpTA==}
peerDependencies:
react: '*'
react-dom: '*'
dependencies:
'@popperjs/core': 2.11.8
classnames: 2.3.2
prism-react-renderer: 1.3.5(react@18.2.0)
rc-calendar: 9.15.11(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-modal: 3.16.1(react-dom@18.2.0)(react@18.2.0)
react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0)
react-select: 3.2.0(react-dom@18.2.0)(react@18.2.0)
react-textarea-autosize: 7.1.2(react@18.2.0)
dev: false
/plaid@10.9.0:
resolution: {integrity: sha512-Dhe4+krfCpDem3jhEqWQLkqiKcOOOkaox2nW1nZtZimsyfyrJ0QPQQWAUdo8IJet4izpR/ecqW+eGLlLdfdl+Q==}
engines: {node: '>=10.0.0'}
dependencies:
axios: 0.21.4
transitivePeerDependencies:
- debug
dev: false
/plaid@9.12.0:
resolution: {integrity: sha512-Gc6rhfEUakAjPWUOdmcZ2axZyurHgKLxx892FLkQEQhOqh5k8uZpbNxPBGOk+Uhh75wWfwWLjokPUnJb93bjwQ==}
engines: {node: '>=10.0.0'}
dependencies:
axios: 0.21.4
transitivePeerDependencies:
- debug
dev: false
/playwright-core@1.36.1:
resolution: {integrity: sha512-7+tmPuMcEW4xeCL9cp9KxmYpQYHKkyjwoXRnoeTowaeNat8PoBMk/HwCYhqkH2fRkshfKEOiVus/IhID2Pg8kg==}
engines: {node: '>=16'}
@@ -20091,6 +20282,14 @@ packages:
engines: {node: '>= 0.8'}
dev: false
/prism-react-renderer@1.3.5(react@18.2.0):
resolution: {integrity: sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==}
peerDependencies:
react: '>=0.14.9'
dependencies:
react: 18.2.0
dev: false
/proc-log@2.0.1:
resolution: {integrity: sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -20657,6 +20856,72 @@ packages:
unpipe: 1.0.0
dev: false
/rc-align@2.4.5:
resolution: {integrity: sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw==}
dependencies:
babel-runtime: 6.26.0
dom-align: 1.12.4
prop-types: 15.8.1
rc-util: 4.21.1
dev: false
/rc-animate@2.11.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-1NyuCGFJG/0Y+9RKh5y/i/AalUCA51opyyS/jO2seELpgymZm2u9QV3xwODwEuzkmeQ1BDPxMLmYLcTJedPlkQ==}
peerDependencies:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
babel-runtime: 6.26.0
classnames: 2.3.2
css-animation: 1.6.1
prop-types: 15.8.1
raf: 3.4.1
rc-util: 4.21.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-lifecycles-compat: 3.0.4
dev: false
/rc-calendar@9.15.11(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-qv0VXfAAnysMWJigxaP6se4bJHvr17D9qsLbi8BOpdgEocsS0RkgY1IUiFaOVYKJDy/EyLC447O02sV/y5YYBg==}
dependencies:
babel-runtime: 6.26.0
classnames: 2.3.2
moment: 2.29.4
prop-types: 15.8.1
rc-trigger: 2.6.5(react-dom@18.2.0)(react@18.2.0)
rc-util: 4.21.1
react-lifecycles-compat: 3.0.4
transitivePeerDependencies:
- react
- react-dom
dev: false
/rc-trigger@2.6.5(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-m6Cts9hLeZWsTvWnuMm7oElhf+03GOjOLfTuU0QmdB9ZrW7jR2IpI5rpNM7i9MvAAlMAmTx5Zr7g3uu/aMvZAw==}
dependencies:
babel-runtime: 6.26.0
classnames: 2.3.2
prop-types: 15.8.1
rc-align: 2.4.5
rc-animate: 2.11.1(react-dom@18.2.0)(react@18.2.0)
rc-util: 4.21.1
react-lifecycles-compat: 3.0.4
transitivePeerDependencies:
- react
- react-dom
dev: false
/rc-util@4.21.1:
resolution: {integrity: sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==}
dependencies:
add-dom-event-listener: 1.1.0
prop-types: 15.8.1
react-is: 16.13.1
react-lifecycles-compat: 3.0.4
shallowequal: 1.1.0
dev: false
/rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
@@ -20879,6 +21144,15 @@ packages:
react: 18.2.0
dev: false
/react-input-autosize@3.0.0(react@18.2.0):
resolution: {integrity: sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==}
peerDependencies:
react: ^16.3.0 || ^17.0.0
dependencies:
prop-types: 15.8.1
react: 18.2.0
dev: false
/react-intl-universal@2.6.11(react@18.2.0):
resolution: {integrity: sha512-myFwndeCqo+02Xm43RnO+gDltDnfYnVrm/09RzC/Ski3vjVNpyoV5edmigAjo12x+qnhuL+5zgVvqW+9BuMMBw==}
requiresBuild: true
@@ -20921,6 +21195,33 @@ packages:
react: 18.2.0
dev: false
/react-modal@3.16.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==}
engines: {node: '>=8'}
peerDependencies:
react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
dependencies:
exenv: 1.2.2
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-lifecycles-compat: 3.0.4
warning: 4.0.3
dev: false
/react-plaid-link@3.5.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OSbPVEIQY3RDroDGyimRh9vUpZfSVzKVCwrbGOSIjcmluHnPKTkvJ1BnYbvE7kH+v8urJXMHloV43uMTNY3SLg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-script-hook: 1.7.2(react-dom@18.2.0)(react@18.2.0)
dev: false
/react-popper@1.3.11(react@18.2.0):
resolution: {integrity: sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==}
peerDependencies:
@@ -21063,6 +21364,16 @@ packages:
tiny-warning: 1.0.3
dev: false
/react-script-hook@1.7.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-fhyCEfXb94fag34UPRF0zry1XGwmVY+79iibWwTqAoOiCzYJQOYTiWJ7CnqglA9tMSV8g45cQpHCMcBwr7dwhA==}
peerDependencies:
react: ^16.8.6 || 17 - 18
react-dom: ^16.8.6 || 17 - 18
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-scripts@5.0.1(@babel/plugin-syntax-flow@7.22.5)(@babel/plugin-transform-react-jsx@7.20.13)(eslint@8.33.0)(react@18.2.0)(sass@1.68.0)(ts-node@10.9.1)(typescript@4.9.5):
resolution: {integrity: sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==}
engines: {node: '>=14.0.0'}
@@ -21185,6 +21496,24 @@ packages:
- react-dom
dev: false
/react-select@3.2.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-B/q3TnCZXEKItO0fFN/I0tWOX3WJvi/X2wtdffmwSQVRwg5BpValScTO1vdic9AxlUgmeSzib2hAZAwIUQUZGQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
dependencies:
'@babel/runtime': 7.20.13
'@emotion/cache': 10.0.29
'@emotion/core': 10.3.1(react@18.2.0)
'@emotion/css': 10.0.27
memoize-one: 5.2.1
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-input-autosize: 3.0.0(react@18.2.0)
react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
dev: false
/react-side-effect@2.1.2(react@18.2.0):
resolution: {integrity: sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==}
peerDependencies:
@@ -21240,6 +21569,16 @@ packages:
react: 18.2.0
dev: false
/react-textarea-autosize@7.1.2(react@18.2.0):
resolution: {integrity: sha512-uH3ORCsCa3C6LHxExExhF4jHoXYCQwE5oECmrRsunlspaDAbS4mGKNlWZqjLfInWtFQcf0o1n1jC/NGXFdUBCg==}
peerDependencies:
react: '>=0.14.0 <17.0.0'
dependencies:
'@babel/runtime': 7.20.13
prop-types: 15.8.1
react: 18.2.0
dev: false
/react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==}
peerDependencies:
@@ -21597,6 +21936,10 @@ packages:
resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
dev: false
/regenerator-runtime@0.11.1:
resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==}
dev: false
/regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}