@huma-finance/widgets 0.0.59-beta.480 → 0.0.59-beta.482

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,361 @@
1
+ import {
2
+ checkIsDev,
3
+ IdentityServiceV2,
4
+ IdentityVerificationStatusV2,
5
+ KYCCopy,
6
+ PoolInfoV2,
7
+ TrancheType,
8
+ useAuthErrorHandling,
9
+ VerificationStatusResultV2,
10
+ } from '@huma-finance/shared'
11
+ import { Box, css, useTheme } from '@mui/material'
12
+ import { useWeb3React } from '@web3-react/core'
13
+ import React, { useCallback, useEffect, useRef, useState } from 'react'
14
+
15
+ import Persona, { Client } from 'persona'
16
+ import { useAppDispatch } from '../../../../hooks/useRedux'
17
+ import { setError, setStep } from '../../../../store/widgets.reducers'
18
+ import { BottomButton } from '../../../BottomButton'
19
+ import { ApproveLenderImg } from '../../../images'
20
+ import { LoadingModal } from '../../../LoadingModal'
21
+ import { WrapperModal } from '../../../WrapperModal'
22
+ import { WIDGET_STEP } from '../../../../store/widgets.store'
23
+
24
+ const LoadingCopiesByType: {
25
+ [key: string]: {
26
+ description: string
27
+ }
28
+ } = {
29
+ verificationStatus: {
30
+ description: `Checking your verification status...`,
31
+ },
32
+ startKYC: {
33
+ description: `Starting KYC...`,
34
+ },
35
+ approveLender: {
36
+ description: `Approving as lender...`,
37
+ },
38
+ }
39
+
40
+ type Props = {
41
+ poolInfo: PoolInfoV2
42
+ isUniTranche: boolean
43
+ changeTranche: (tranche: TrancheType) => void
44
+ handleClose: () => void
45
+ }
46
+
47
+ export function PersonaEvaluation({
48
+ poolInfo,
49
+ isUniTranche,
50
+ changeTranche,
51
+ handleClose,
52
+ }: Props): React.ReactElement | null {
53
+ const theme = useTheme()
54
+ const isDev = checkIsDev()
55
+ const dispatch = useAppDispatch()
56
+ const { account, chainId } = useWeb3React()
57
+ const {
58
+ errorType,
59
+ setError: setAuthError,
60
+ isWalletOwnershipVerified,
61
+ isWalletOwnershipVerificationRequired,
62
+ } = useAuthErrorHandling(isDev)
63
+ const KYCCopies = poolInfo.KYC!.Persona!
64
+ const [loadingType, setLoadingType] = useState<
65
+ 'verificationStatus' | 'startKYC' | 'approveLender' | undefined
66
+ >()
67
+ const [kycCopy, setKYCCopy] = useState<KYCCopy>(KYCCopies.verifyIdentity)
68
+ const [inquiryId, setInquiryId] = useState<string | undefined>()
69
+ const [sessionToken, setSessionToken] = useState<string | undefined>()
70
+ const [verificationStatus, setVerificationStatus] = useState<
71
+ VerificationStatusResultV2 | undefined
72
+ >()
73
+ const [showPersonaClient, setShowPersonaClient] = useState<boolean>(false)
74
+ const isKYCCompletedRef = useRef<boolean>(false)
75
+ const isActionOngoingRef = useRef<boolean>(false)
76
+ const isKYCResumedRef = useRef<boolean>(false)
77
+
78
+ const approveLender = useCallback(async () => {
79
+ try {
80
+ isActionOngoingRef.current = true
81
+ setLoadingType('approveLender')
82
+ await IdentityServiceV2.approveLender(
83
+ account!,
84
+ chainId!,
85
+ poolInfo.juniorTrancheVault,
86
+ isDev,
87
+ )
88
+ if (!isUniTranche) {
89
+ await IdentityServiceV2.approveLender(
90
+ account!,
91
+ chainId!,
92
+ poolInfo.seniorTrancheVault,
93
+ isDev,
94
+ )
95
+ }
96
+ if (isUniTranche) {
97
+ changeTranche('junior')
98
+ dispatch(setStep(WIDGET_STEP.ChooseAmount))
99
+ } else {
100
+ dispatch(setStep(WIDGET_STEP.ChooseTranche))
101
+ }
102
+ } finally {
103
+ isActionOngoingRef.current = false
104
+ setLoadingType(undefined)
105
+ }
106
+ }, [
107
+ account,
108
+ chainId,
109
+ changeTranche,
110
+ dispatch,
111
+ isDev,
112
+ isUniTranche,
113
+ poolInfo.juniorTrancheVault,
114
+ poolInfo.seniorTrancheVault,
115
+ ])
116
+
117
+ const checkVerificationStatus = useCallback(async () => {
118
+ if (isActionOngoingRef.current || isKYCResumedRef.current) {
119
+ return
120
+ }
121
+ try {
122
+ if (account && chainId) {
123
+ isActionOngoingRef.current = true
124
+ setLoadingType('verificationStatus')
125
+ const verificationStatus =
126
+ await IdentityServiceV2.getVerificationStatusV2(
127
+ account,
128
+ chainId,
129
+ isDev,
130
+ )
131
+ setVerificationStatus(verificationStatus)
132
+ setInquiryId(verificationStatus.personaInquiryId)
133
+
134
+ switch (verificationStatus.status) {
135
+ case IdentityVerificationStatusV2.NOT_STARTED: {
136
+ const startVerificationResult =
137
+ await IdentityServiceV2.startVerification(account, chainId, isDev)
138
+ setInquiryId(startVerificationResult.personaInquiryId)
139
+ setKYCCopy(KYCCopies.verifyIdentity)
140
+ setLoadingType(undefined)
141
+ break
142
+ }
143
+
144
+ case IdentityVerificationStatusV2.CREATED: {
145
+ setKYCCopy(KYCCopies.verifyIdentity)
146
+ setLoadingType(undefined)
147
+ break
148
+ }
149
+
150
+ case IdentityVerificationStatusV2.PENDING: {
151
+ if (!isKYCCompletedRef.current) {
152
+ const resumeVerificationResult =
153
+ await IdentityServiceV2.resumeVerification(
154
+ account,
155
+ chainId,
156
+ isDev,
157
+ )
158
+ verificationStatus.status = resumeVerificationResult.status
159
+ setVerificationStatus(verificationStatus)
160
+ setSessionToken(resumeVerificationResult.sessionToken)
161
+ setLoadingType(undefined)
162
+ isKYCResumedRef.current = true
163
+ } else {
164
+ setLoadingType('verificationStatus')
165
+ }
166
+ setKYCCopy(KYCCopies.verifyIdentity)
167
+ break
168
+ }
169
+
170
+ case IdentityVerificationStatusV2.EXPIRED: {
171
+ const resumeVerificationResult =
172
+ await IdentityServiceV2.resumeVerification(
173
+ account,
174
+ chainId,
175
+ isDev,
176
+ )
177
+ verificationStatus.status = resumeVerificationResult.status
178
+ setVerificationStatus(verificationStatus)
179
+ setSessionToken(resumeVerificationResult.sessionToken)
180
+ setKYCCopy(KYCCopies.verifyIdentity)
181
+ setLoadingType(undefined)
182
+ isKYCResumedRef.current = true
183
+ break
184
+ }
185
+
186
+ case IdentityVerificationStatusV2.APPROVED: {
187
+ await approveLender()
188
+ break
189
+ }
190
+
191
+ case IdentityVerificationStatusV2.DECLINED: {
192
+ setKYCCopy(KYCCopies.verificationDeclined)
193
+ setLoadingType(undefined)
194
+ break
195
+ }
196
+
197
+ case IdentityVerificationStatusV2.NEEDS_REVIEW: {
198
+ setKYCCopy(KYCCopies.verificationNeedsReview)
199
+ setLoadingType(undefined)
200
+ break
201
+ }
202
+
203
+ default:
204
+ break
205
+ }
206
+ }
207
+ } catch (e: unknown) {
208
+ setAuthError(e)
209
+ } finally {
210
+ isActionOngoingRef.current = false
211
+ }
212
+ }, [KYCCopies, account, approveLender, chainId, isDev, setAuthError])
213
+
214
+ useEffect(() => {
215
+ checkVerificationStatus()
216
+ }, [checkVerificationStatus])
217
+
218
+ useEffect(() => {
219
+ if (isWalletOwnershipVerificationRequired && isWalletOwnershipVerified) {
220
+ checkVerificationStatus()
221
+ }
222
+ }, [
223
+ checkVerificationStatus,
224
+ isWalletOwnershipVerificationRequired,
225
+ isWalletOwnershipVerified,
226
+ ])
227
+
228
+ useEffect(() => {
229
+ const interval = setInterval(() => {
230
+ if (
231
+ verificationStatus?.status &&
232
+ [
233
+ IdentityVerificationStatusV2.CREATED,
234
+ IdentityVerificationStatusV2.PENDING,
235
+ ].includes(verificationStatus.status)
236
+ ) {
237
+ checkVerificationStatus()
238
+ }
239
+ }, 10 * 1000)
240
+ // eslint-disable-next-line consistent-return
241
+ return () => clearInterval(interval)
242
+ }, [checkVerificationStatus, verificationStatus?.status])
243
+
244
+ useEffect(() => {
245
+ if (errorType === 'NotSignedIn') {
246
+ setLoadingType(undefined)
247
+ setKYCCopy(KYCCopies.signInRequired)
248
+ } else if (errorType === 'UserRejected') {
249
+ dispatch(
250
+ setError({
251
+ errorMessage: 'User has rejected the transaction.',
252
+ }),
253
+ )
254
+ } else if (errorType === 'Other') {
255
+ dispatch(
256
+ setError({
257
+ errorMessage: 'Something went wrong. Please try again later.',
258
+ }),
259
+ )
260
+ }
261
+ }, [KYCCopies.signInRequired, dispatch, errorType])
262
+
263
+ useEffect(() => {
264
+ const modalElement = document.getElementById('huma-modal')
265
+ if (modalElement) {
266
+ modalElement.style.visibility = showPersonaClient ? 'hidden' : 'visible'
267
+ }
268
+ }, [showPersonaClient])
269
+
270
+ const startKYC = async () => {
271
+ isActionOngoingRef.current = true
272
+ isKYCResumedRef.current = false
273
+ setLoadingType('startKYC')
274
+ const client: Client = new Persona.Client({
275
+ inquiryId,
276
+ sessionToken,
277
+ onReady: () => {
278
+ client.open()
279
+ setShowPersonaClient(true)
280
+ },
281
+ onComplete: () => {
282
+ isKYCCompletedRef.current = true
283
+ isActionOngoingRef.current = false
284
+ setShowPersonaClient(false)
285
+ checkVerificationStatus()
286
+ },
287
+ onCancel: () => {
288
+ isActionOngoingRef.current = false
289
+ setShowPersonaClient(false)
290
+ setLoadingType(undefined)
291
+ },
292
+ onError: () => {
293
+ isActionOngoingRef.current = false
294
+ setShowPersonaClient(false)
295
+ setLoadingType(undefined)
296
+ },
297
+ })
298
+ }
299
+
300
+ const handleAction = () => {
301
+ if (verificationStatus) {
302
+ switch (verificationStatus.status) {
303
+ case IdentityVerificationStatusV2.NOT_STARTED:
304
+ case IdentityVerificationStatusV2.CREATED:
305
+ case IdentityVerificationStatusV2.PENDING:
306
+ case IdentityVerificationStatusV2.EXPIRED:
307
+ startKYC()
308
+ break
309
+
310
+ case IdentityVerificationStatusV2.DECLINED:
311
+ case IdentityVerificationStatusV2.NEEDS_REVIEW:
312
+ handleClose()
313
+ break
314
+
315
+ default:
316
+ break
317
+ }
318
+ }
319
+ }
320
+
321
+ const styles = {
322
+ iconWrapper: css`
323
+ ${theme.cssMixins.rowCentered};
324
+ margin-top: ${theme.spacing(8)};
325
+ & > img {
326
+ width: 144px;
327
+ }
328
+ `,
329
+ description: css`
330
+ ${theme.cssMixins.rowCentered};
331
+ margin-top: ${theme.spacing(10)};
332
+ padding: ${theme.spacing(0, 2)};
333
+ font-family: 'Uni-Neue-Regular';
334
+ font-size: 16px;
335
+ color: #a8a1b2;
336
+ `,
337
+ }
338
+
339
+ if (!loadingType) {
340
+ return (
341
+ <WrapperModal title={kycCopy.title}>
342
+ <Box css={styles.iconWrapper}>
343
+ <img src={ApproveLenderImg} alt='approve-lender' />
344
+ </Box>
345
+ <Box css={styles.description}>{kycCopy.description}</Box>
346
+ {Boolean(kycCopy.buttonText) && (
347
+ <BottomButton variant='contained' onClick={handleAction}>
348
+ {kycCopy.buttonText}
349
+ </BottomButton>
350
+ )}
351
+ </WrapperModal>
352
+ )
353
+ }
354
+
355
+ return (
356
+ <LoadingModal
357
+ title='Lender Approval'
358
+ description={LoadingCopiesByType[loadingType].description}
359
+ />
360
+ )
361
+ }
@@ -0,0 +1,345 @@
1
+ import {
2
+ CHAINS,
3
+ checkIsDev,
4
+ configUtil,
5
+ DocSignatureStatus,
6
+ IdentityService,
7
+ KYC_PROVIDER,
8
+ KYCCopy,
9
+ PoolInfoV2,
10
+ timeUtil,
11
+ useAuthErrorHandling,
12
+ useParamsSearch,
13
+ VerificationStatusResult,
14
+ } from '@huma-finance/shared'
15
+ import { Box, css, useTheme } from '@mui/material'
16
+ import { useWeb3React } from '@web3-react/core'
17
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
18
+
19
+ import { useAppDispatch } from '../../../../hooks/useRedux'
20
+ import { setError } from '../../../../store/widgets.reducers'
21
+ import { BottomButton } from '../../../BottomButton'
22
+ import { HumaSnackBar } from '../../../HumaSnackBar'
23
+ import { ApproveLenderImg } from '../../../images'
24
+ import { LoadingModal } from '../../../LoadingModal'
25
+ import { WrapperModal } from '../../../WrapperModal'
26
+
27
+ const LoadingCopiesByType: {
28
+ [key: string]: {
29
+ description: string
30
+ }
31
+ } = {
32
+ verificationStatus: {
33
+ description: `Checking your verification status...`,
34
+ },
35
+ sendDocSignatureLink: {
36
+ description: `Sending signature link...`,
37
+ },
38
+ }
39
+
40
+ type Props = {
41
+ poolInfo: PoolInfoV2
42
+ handleClose: () => void
43
+ }
44
+
45
+ export function SecuritizeEvaluation({
46
+ poolInfo,
47
+ handleClose,
48
+ }: Props): React.ReactElement | null {
49
+ const theme = useTheme()
50
+ const isDev = checkIsDev()
51
+ const dispatch = useAppDispatch()
52
+ const { account, chainId } = useWeb3React()
53
+ const { kycProvider, code, kycPool } = useParamsSearch()
54
+ const {
55
+ isWalletOwnershipVerified,
56
+ setError: setAuthError,
57
+ error: authError,
58
+ } = useAuthErrorHandling(isDev)
59
+ const [loadingType, setLoadingType] = useState<
60
+ 'verificationStatus' | 'sendDocSignatureLink'
61
+ >()
62
+ const KYCCopies = poolInfo.KYC!.Securitize!
63
+ const [kycCopy, setKYCCopy] = useState<KYCCopy>(KYCCopies.verifyIdentity)
64
+ const [KYCVerifyStatus, setKYCVerifyStatus] =
65
+ useState<VerificationStatusResult>()
66
+ const [docSignatureStatus, setDocSignatureStatus] =
67
+ useState<DocSignatureStatus['status']>()
68
+ const docSignatureCompleted = docSignatureStatus === 'completed'
69
+ const [openSnackBar, setOpenSnackBar] = useState<boolean>(false)
70
+
71
+ const { envelopeKey, envelopeLastQueryTimeKey, envelopeDocuSignStatusKey } =
72
+ useMemo(() => {
73
+ const envelopeKey = `${poolInfo.pool.toLowerCase()}-${account?.toLowerCase()}`
74
+ const envelopeLastQueryTimeKey = `${envelopeKey}-last-query-time`
75
+ const envelopeDocuSignStatusKey = `${envelopeKey}-docuSign-status`
76
+ return {
77
+ envelopeKey,
78
+ envelopeLastQueryTimeKey,
79
+ envelopeDocuSignStatusKey,
80
+ }
81
+ }, [account, poolInfo.pool])
82
+
83
+ // DocuSign GET requests to any specific URL can be called not more often than once every 15 minutes.
84
+ // https://developers.docusign.com/platform/api-guidelines/#:~:text=Polling,not%20once%20every%2010%20minutes
85
+ const checkGetDocSignatureStatusIsAvailable = useCallback(() => {
86
+ const lastQueryTime = localStorage.getItem(envelopeLastQueryTimeKey)
87
+ if (!lastQueryTime) {
88
+ return true
89
+ }
90
+ const lastQueryTimeNumber = Number(lastQueryTime)
91
+ const now = timeUtil.getUnixTimestamp()
92
+ const diff = now - lastQueryTimeNumber
93
+ const fifteenMinutes = 15 * 60
94
+ return diff > fifteenMinutes
95
+ }, [envelopeLastQueryTimeKey])
96
+
97
+ useEffect(() => {
98
+ setKYCCopy(KYCCopies.verifyIdentity)
99
+ const docuSignStatus = localStorage.getItem(envelopeDocuSignStatusKey)
100
+ setDocSignatureStatus(docuSignStatus as DocSignatureStatus['status'])
101
+ if (docuSignStatus === 'completed') {
102
+ setKYCCopy(KYCCopies.docUnderReview!)
103
+ return
104
+ }
105
+
106
+ const fetchData = async () => {
107
+ try {
108
+ if (kycProvider && kycPool && account && chainId) {
109
+ setLoadingType('verificationStatus')
110
+ await IdentityService.onboard(
111
+ account,
112
+ code as string,
113
+ kycPool as string,
114
+ chainId,
115
+ isDev,
116
+ )
117
+ }
118
+ } catch (e) {
119
+ try {
120
+ setAuthError(e)
121
+ setKYCCopy(KYCCopies.signInRequired)
122
+ } catch (e) {
123
+ // The repeated call will throw an error of 401, so we can ignore it.
124
+ console.log(e)
125
+ }
126
+ }
127
+
128
+ try {
129
+ if (account && chainId) {
130
+ setLoadingType('verificationStatus')
131
+ const verificationStatus =
132
+ await IdentityService.getVerificationStatus(
133
+ account,
134
+ poolInfo.pool,
135
+ chainId,
136
+ isDev,
137
+ )
138
+ setKYCVerifyStatus(verificationStatus)
139
+ if (verificationStatus.isVerified) {
140
+ const envelopeId = localStorage.getItem(envelopeKey)
141
+ if (!envelopeId) {
142
+ setKYCCopy(KYCCopies.emailSignatureLink!)
143
+ } else if (!checkGetDocSignatureStatusIsAvailable()) {
144
+ setKYCCopy(KYCCopies.resendSignatureLink!)
145
+ } else {
146
+ const { status } = await IdentityService.getDocSignatureStatus(
147
+ envelopeId,
148
+ chainId,
149
+ isDev,
150
+ )
151
+ localStorage.setItem(
152
+ envelopeLastQueryTimeKey,
153
+ String(timeUtil.getUnixTimestamp()),
154
+ )
155
+ localStorage.setItem(envelopeDocuSignStatusKey, status)
156
+ setDocSignatureStatus(status)
157
+ if (status === 'completed') {
158
+ setKYCCopy(KYCCopies.docUnderReview!)
159
+ // For voided and declined status, we need to send a new link
160
+ } else if (['voided', 'declined'].includes(status)) {
161
+ localStorage.removeItem(envelopeKey)
162
+ localStorage.removeItem(envelopeLastQueryTimeKey)
163
+ setKYCCopy(KYCCopies.emailSignatureLink!)
164
+ } else {
165
+ setKYCCopy(KYCCopies.resendSignatureLink!)
166
+ }
167
+ }
168
+ } else {
169
+ setKYCCopy(KYCCopies.verifyIdentity)
170
+ }
171
+ }
172
+ } catch (e: unknown) {
173
+ try {
174
+ setAuthError(e)
175
+ setKYCCopy(KYCCopies.signInRequired)
176
+ } catch (e) {
177
+ console.error(e)
178
+ dispatch(
179
+ setError({
180
+ errorMessage: 'Something went wrong, please try again later.',
181
+ }),
182
+ )
183
+ }
184
+ } finally {
185
+ setLoadingType(undefined)
186
+ }
187
+ }
188
+ fetchData()
189
+ }, [
190
+ KYCCopies.docUnderReview,
191
+ KYCCopies.emailSignatureLink,
192
+ KYCCopies.resendSignatureLink,
193
+ KYCCopies.signInRequired,
194
+ KYCCopies.verifyIdentity,
195
+ account,
196
+ chainId,
197
+ checkGetDocSignatureStatusIsAvailable,
198
+ code,
199
+ dispatch,
200
+ envelopeDocuSignStatusKey,
201
+ envelopeKey,
202
+ envelopeLastQueryTimeKey,
203
+ isDev,
204
+ kycPool,
205
+ kycProvider,
206
+ poolInfo.pool,
207
+ setAuthError,
208
+ isWalletOwnershipVerified,
209
+ ])
210
+
211
+ useEffect(() => {
212
+ if (
213
+ authError &&
214
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
215
+ [4001, 'ACTION_REJECTED'].includes((authError as any).code)
216
+ ) {
217
+ dispatch(
218
+ setError({
219
+ errorMessage: 'User has rejected the transaction.',
220
+ }),
221
+ )
222
+ }
223
+ }, [authError, dispatch])
224
+
225
+ const approveLender = async () => {
226
+ if (docSignatureCompleted) {
227
+ handleClose()
228
+ return
229
+ }
230
+
231
+ const { isNotOnboarded, isVerified } = KYCVerifyStatus || {}
232
+
233
+ if (!isVerified) {
234
+ const issuerId = CHAINS[chainId!].isTestnet
235
+ ? '53a66b32-583e-40e7-ba90-baf516d2cadd'
236
+ : '5557baf5-d3c2-4c80-b522-c05a11c6e586'
237
+ const baseUrl = configUtil.getKYCProviderBaseUrl(
238
+ KYC_PROVIDER.Securitize,
239
+ chainId!,
240
+ )
241
+ const originUrl = window.location.href.split('?')[0]
242
+ const redirectUrl = `${originUrl}?poolName=${poolInfo.poolName}&kycProvider=${KYC_PROVIDER.Securitize}&kycPool=${poolInfo.pool}`
243
+ const providerAuthorizeUrl = `${baseUrl}/#/authorize?issuerId=${issuerId}&scope=details&details=verification&redirectUrl=${redirectUrl}`
244
+ window.location.href = isNotOnboarded ? providerAuthorizeUrl : baseUrl
245
+ } else {
246
+ const envelopeId = localStorage.getItem(envelopeKey)
247
+ try {
248
+ setLoadingType('sendDocSignatureLink')
249
+ if (!envelopeId) {
250
+ const { envelopeId } = await IdentityService.requestDocSignature(
251
+ account!,
252
+ chainId!,
253
+ isDev,
254
+ )
255
+ localStorage.setItem(envelopeKey, envelopeId)
256
+ localStorage.setItem(
257
+ envelopeLastQueryTimeKey,
258
+ String(timeUtil.getUnixTimestamp()),
259
+ )
260
+ setOpenSnackBar(true)
261
+ setKYCCopy(KYCCopies.resendSignatureLink)
262
+ } else {
263
+ await IdentityService.resendDocSignatureLink(
264
+ envelopeId,
265
+ chainId!,
266
+ isDev,
267
+ )
268
+ setOpenSnackBar(true)
269
+ }
270
+ } catch (e: unknown) {
271
+ try {
272
+ const { envelopeId } = await IdentityService.requestDocSignature(
273
+ account!,
274
+ chainId!,
275
+ isDev,
276
+ )
277
+ localStorage.setItem(envelopeKey, envelopeId)
278
+ localStorage.setItem(
279
+ envelopeLastQueryTimeKey,
280
+ String(timeUtil.getUnixTimestamp()),
281
+ )
282
+ setOpenSnackBar(true)
283
+ setKYCCopy(KYCCopies.resendSignatureLink)
284
+ } catch (e) {
285
+ setAuthError(e)
286
+ setKYCCopy(KYCCopies.signInRequired)
287
+ }
288
+ } finally {
289
+ setLoadingType(undefined)
290
+ }
291
+ }
292
+ }
293
+
294
+ const getEmailLinkSentSnackbar = () => (
295
+ <HumaSnackBar
296
+ open={openSnackBar}
297
+ title='Signature Link Sent'
298
+ message='The secure signature session link has been sent to you via email.'
299
+ severity='success'
300
+ onClose={() => setOpenSnackBar(false)}
301
+ />
302
+ )
303
+
304
+ const styles = {
305
+ iconWrapper: css`
306
+ ${theme.cssMixins.rowCentered};
307
+ margin-top: ${theme.spacing(8)};
308
+ & > img {
309
+ width: 144px;
310
+ }
311
+ `,
312
+ description: css`
313
+ ${theme.cssMixins.rowCentered};
314
+ margin-top: ${theme.spacing(10)};
315
+ padding: ${theme.spacing(0, 2)};
316
+ font-family: 'Uni-Neue-Regular';
317
+ font-size: 16px;
318
+ color: #a8a1b2;
319
+ `,
320
+ }
321
+
322
+ if (!loadingType) {
323
+ return (
324
+ <WrapperModal title={kycCopy.title}>
325
+ <Box css={styles.iconWrapper}>
326
+ <img src={ApproveLenderImg} alt='approve-lender' />
327
+ </Box>
328
+ <Box css={styles.description}>{kycCopy.description}</Box>
329
+ {Boolean(kycCopy.buttonText) && (
330
+ <BottomButton variant='contained' onClick={approveLender}>
331
+ {kycCopy.buttonText}
332
+ </BottomButton>
333
+ )}
334
+ {getEmailLinkSentSnackbar()}
335
+ </WrapperModal>
336
+ )
337
+ }
338
+
339
+ return (
340
+ <LoadingModal
341
+ title='Lender Approval'
342
+ description={LoadingCopiesByType[loadingType].description}
343
+ />
344
+ )
345
+ }