@diviswap/sdk 1.7.6
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.
- package/LICENSE +21 -0
- package/README.md +510 -0
- package/bin/create-diviswap-app.js +25 -0
- package/bin/diviswap-sdk.js +4 -0
- package/dist/cli/index.js +1888 -0
- package/dist/cli/templates/nextjs-app/actions.ts.hbs +259 -0
- package/dist/cli/templates/nextjs-app/api-hooks.ts.hbs +439 -0
- package/dist/cli/templates/nextjs-app/api-route.ts.hbs +502 -0
- package/dist/cli/templates/nextjs-app/auth-context.tsx.hbs +59 -0
- package/dist/cli/templates/nextjs-app/client.ts.hbs +116 -0
- package/dist/cli/templates/nextjs-app/dashboard-hooks.ts.hbs +180 -0
- package/dist/cli/templates/nextjs-app/example-page.tsx.hbs +276 -0
- package/dist/cli/templates/nextjs-app/hooks.ts.hbs +252 -0
- package/dist/cli/templates/nextjs-app/kyc-hooks.ts.hbs +87 -0
- package/dist/cli/templates/nextjs-app/kyc-wizard.css.hbs +433 -0
- package/dist/cli/templates/nextjs-app/kyc-wizard.tsx.hbs +711 -0
- package/dist/cli/templates/nextjs-app/layout-wrapper.tsx.hbs +13 -0
- package/dist/cli/templates/nextjs-app/layout.tsx.hbs +13 -0
- package/dist/cli/templates/nextjs-app/middleware.ts.hbs +49 -0
- package/dist/cli/templates/nextjs-app/provider-wrapper.tsx.hbs +8 -0
- package/dist/cli/templates/nextjs-app/provider.tsx.hbs +408 -0
- package/dist/cli/templates/nextjs-app/setup-provider.tsx.hbs +25 -0
- package/dist/cli/templates/nextjs-app/types.ts.hbs +159 -0
- package/dist/cli/templates/react/api-client-wrapper.ts.hbs +89 -0
- package/dist/cli/templates/react/example.tsx.hbs +69 -0
- package/dist/cli/templates/react/tanstack-hooks.ts.hbs +185 -0
- package/dist/cli/templates/webhooks/nextjs.hbs +98 -0
- package/dist/index.d.mts +91 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +2339 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2313 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +192 -0
- package/dist/react/index.d.ts +192 -0
- package/dist/react/index.js +1083 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +1064 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/wallet-BEGvzNtB.d.mts +1614 -0
- package/dist/wallet-BEGvzNtB.d.ts +1614 -0
- package/package.json +102 -0
- package/src/cli/templates/index.ts +65 -0
- package/src/cli/templates/nextjs-app/actions.ts.hbs +259 -0
- package/src/cli/templates/nextjs-app/api-hooks.ts.hbs +439 -0
- package/src/cli/templates/nextjs-app/api-route.ts.hbs +502 -0
- package/src/cli/templates/nextjs-app/auth-context.tsx.hbs +59 -0
- package/src/cli/templates/nextjs-app/client.ts.hbs +116 -0
- package/src/cli/templates/nextjs-app/dashboard-hooks.ts.hbs +180 -0
- package/src/cli/templates/nextjs-app/example-page.tsx.hbs +276 -0
- package/src/cli/templates/nextjs-app/hooks.ts.hbs +252 -0
- package/src/cli/templates/nextjs-app/kyc-hooks.ts.hbs +87 -0
- package/src/cli/templates/nextjs-app/kyc-wizard.css.hbs +433 -0
- package/src/cli/templates/nextjs-app/kyc-wizard.tsx.hbs +711 -0
- package/src/cli/templates/nextjs-app/layout-wrapper.tsx.hbs +13 -0
- package/src/cli/templates/nextjs-app/layout.tsx.hbs +13 -0
- package/src/cli/templates/nextjs-app/middleware.ts.hbs +49 -0
- package/src/cli/templates/nextjs-app/provider-wrapper.tsx.hbs +8 -0
- package/src/cli/templates/nextjs-app/provider.tsx.hbs +408 -0
- package/src/cli/templates/nextjs-app/setup-provider.tsx.hbs +25 -0
- package/src/cli/templates/nextjs-app/types.ts.hbs +159 -0
- package/src/cli/templates/react/api-client-wrapper.ts.hbs +89 -0
- package/src/cli/templates/react/example.tsx.hbs +69 -0
- package/src/cli/templates/react/tanstack-hooks.ts.hbs +185 -0
- package/src/cli/templates/shared/client.ts +78 -0
- package/src/cli/templates/webhooks/nextjs.hbs +98 -0
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { ArrowRight, ArrowLeft, CheckCircle, AlertCircle, Loader2 } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
// Types
|
|
7
|
+
type KYCStep = 'intro' | 'personal' | 'identification' | 'address' | 'review' | 'success';
|
|
8
|
+
|
|
9
|
+
interface KycFormData {
|
|
10
|
+
firstName: string;
|
|
11
|
+
lastName: string;
|
|
12
|
+
email: string;
|
|
13
|
+
dobDay: string;
|
|
14
|
+
dobMonth: string;
|
|
15
|
+
dobYear: string;
|
|
16
|
+
idType: 'ssn' | 'tin' | 'passport';
|
|
17
|
+
idNumber: string;
|
|
18
|
+
addressLine1: string;
|
|
19
|
+
addressLine2?: string;
|
|
20
|
+
city: string;
|
|
21
|
+
state: string;
|
|
22
|
+
postalCode: string;
|
|
23
|
+
country: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface LiberexKYCProps {
|
|
27
|
+
onSuccess?: (status: any) => void;
|
|
28
|
+
onError?: (error: Error) => void;
|
|
29
|
+
onClose?: () => void;
|
|
30
|
+
theme?: 'light' | 'dark' | {
|
|
31
|
+
primaryColor?: string;
|
|
32
|
+
backgroundColor?: string;
|
|
33
|
+
textColor?: string;
|
|
34
|
+
borderRadius?: string;
|
|
35
|
+
};
|
|
36
|
+
steps?: KYCStep[];
|
|
37
|
+
autoClose?: boolean;
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function LiberexKYC({
|
|
42
|
+
onSuccess,
|
|
43
|
+
onError,
|
|
44
|
+
onClose,
|
|
45
|
+
theme = 'light',
|
|
46
|
+
steps = ['intro', 'personal', 'identification', 'address', 'review', 'success'],
|
|
47
|
+
autoClose = false,
|
|
48
|
+
className = ''
|
|
49
|
+
}: LiberexKYCProps) {
|
|
50
|
+
const [currentStep, setCurrentStep] = useState<KYCStep>(steps[0]);
|
|
51
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
52
|
+
const [formData, setFormData] = useState<KycFormData>({
|
|
53
|
+
firstName: '',
|
|
54
|
+
lastName: '',
|
|
55
|
+
email: '',
|
|
56
|
+
dobDay: '',
|
|
57
|
+
dobMonth: '',
|
|
58
|
+
dobYear: '',
|
|
59
|
+
idType: 'ssn',
|
|
60
|
+
idNumber: '',
|
|
61
|
+
addressLine1: '',
|
|
62
|
+
addressLine2: '',
|
|
63
|
+
city: '',
|
|
64
|
+
state: '',
|
|
65
|
+
postalCode: '',
|
|
66
|
+
country: 'US'
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Theme configuration
|
|
70
|
+
const themeConfig = typeof theme === 'string' ? {
|
|
71
|
+
primaryColor: theme === 'dark' ? '#6366f1' : '#4f46e5',
|
|
72
|
+
backgroundColor: theme === 'dark' ? '#1f2937' : '#ffffff',
|
|
73
|
+
textColor: theme === 'dark' ? '#f9fafb' : '#1f2937',
|
|
74
|
+
borderRadius: '8px'
|
|
75
|
+
} : theme;
|
|
76
|
+
|
|
77
|
+
const stepIndex = steps.indexOf(currentStep);
|
|
78
|
+
const totalSteps = steps.filter(s => s !== 'intro' && s !== 'success').length;
|
|
79
|
+
const progressPercentage = stepIndex === 0 ? 0 : ((stepIndex - 1) / (totalSteps - 1)) * 100;
|
|
80
|
+
|
|
81
|
+
const submitKYC = async () => {
|
|
82
|
+
setIsSubmitting(true);
|
|
83
|
+
try {
|
|
84
|
+
// Format date of birth
|
|
85
|
+
const dateOfBirth = `${formData.dobYear}-${formData.dobMonth.padStart(2, '0')}-${formData.dobDay.padStart(2, '0')}`;
|
|
86
|
+
|
|
87
|
+
// Prepare KYC data
|
|
88
|
+
const kycData = {
|
|
89
|
+
personalInfo: {
|
|
90
|
+
firstName: formData.firstName,
|
|
91
|
+
lastName: formData.lastName,
|
|
92
|
+
email: formData.email,
|
|
93
|
+
dateOfBirth,
|
|
94
|
+
ssn: formData.idType === 'ssn' ? formData.idNumber.replace(/-/g, '') : undefined,
|
|
95
|
+
tin: formData.idType === 'tin' ? formData.idNumber : undefined,
|
|
96
|
+
passport: formData.idType === 'passport' ? formData.idNumber : undefined,
|
|
97
|
+
},
|
|
98
|
+
address: {
|
|
99
|
+
addressLine1: formData.addressLine1,
|
|
100
|
+
addressLine2: formData.addressLine2,
|
|
101
|
+
city: formData.city,
|
|
102
|
+
state: formData.state,
|
|
103
|
+
postalCode: formData.postalCode,
|
|
104
|
+
country: formData.country,
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Call your KYC submission API
|
|
109
|
+
const response = await fetch('/api/diviswap', {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: { 'Content-Type': 'application/json' },
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
action: 'submitKYC',
|
|
114
|
+
...kycData
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
const error = await response.json();
|
|
120
|
+
throw new Error(error.error || 'KYC submission failed');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = await response.json();
|
|
124
|
+
|
|
125
|
+
if (steps.includes('success')) {
|
|
126
|
+
setCurrentStep('success');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
onSuccess?.(result);
|
|
130
|
+
|
|
131
|
+
if (autoClose) {
|
|
132
|
+
setTimeout(() => onClose?.(), 2000);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
} catch (error: any) {
|
|
136
|
+
console.error('KYC submission error:', error);
|
|
137
|
+
onError?.(error);
|
|
138
|
+
} finally {
|
|
139
|
+
setIsSubmitting(false);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const nextStep = () => {
|
|
144
|
+
const currentIndex = steps.indexOf(currentStep);
|
|
145
|
+
if (currentIndex < steps.length - 1) {
|
|
146
|
+
setCurrentStep(steps[currentIndex + 1]);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const prevStep = () => {
|
|
151
|
+
const currentIndex = steps.indexOf(currentStep);
|
|
152
|
+
if (currentIndex > 0) {
|
|
153
|
+
setCurrentStep(steps[currentIndex - 1]);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const updateFormData = (updates: Partial<KycFormData>) => {
|
|
158
|
+
setFormData(prev => ({ ...prev, ...updates }));
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const containerStyle = {
|
|
162
|
+
backgroundColor: themeConfig.backgroundColor,
|
|
163
|
+
color: themeConfig.textColor,
|
|
164
|
+
borderRadius: themeConfig.borderRadius
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const primaryButtonStyle = {
|
|
168
|
+
backgroundColor: themeConfig.primaryColor,
|
|
169
|
+
borderRadius: themeConfig.borderRadius
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div className={`diviswap-kyc-container ${className}`} style={containerStyle}>
|
|
174
|
+
{/* Progress Bar */}
|
|
175
|
+
{currentStep !== 'intro' && currentStep !== 'success' && (
|
|
176
|
+
<div className="progress-container">
|
|
177
|
+
<div className="progress-bar">
|
|
178
|
+
<div
|
|
179
|
+
className="progress-fill"
|
|
180
|
+
style={{
|
|
181
|
+
width: `${progressPercentage}%`,
|
|
182
|
+
backgroundColor: themeConfig.primaryColor
|
|
183
|
+
}}
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
<div className="progress-text">
|
|
187
|
+
Step {Math.max(stepIndex, 1)} of {totalSteps}
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
)}
|
|
191
|
+
|
|
192
|
+
{/* Step Content */}
|
|
193
|
+
{currentStep === 'intro' && (
|
|
194
|
+
<IntroStep
|
|
195
|
+
onNext={nextStep}
|
|
196
|
+
onClose={onClose}
|
|
197
|
+
theme={themeConfig}
|
|
198
|
+
/>
|
|
199
|
+
)}
|
|
200
|
+
|
|
201
|
+
{currentStep === 'personal' && (
|
|
202
|
+
<PersonalInfoStep
|
|
203
|
+
formData={formData}
|
|
204
|
+
updateFormData={updateFormData}
|
|
205
|
+
onNext={nextStep}
|
|
206
|
+
onPrev={prevStep}
|
|
207
|
+
theme={themeConfig}
|
|
208
|
+
/>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{currentStep === 'identification' && (
|
|
212
|
+
<IdentificationStep
|
|
213
|
+
formData={formData}
|
|
214
|
+
updateFormData={updateFormData}
|
|
215
|
+
onNext={nextStep}
|
|
216
|
+
onPrev={prevStep}
|
|
217
|
+
theme={themeConfig}
|
|
218
|
+
/>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
{currentStep === 'address' && (
|
|
222
|
+
<AddressStep
|
|
223
|
+
formData={formData}
|
|
224
|
+
updateFormData={updateFormData}
|
|
225
|
+
onNext={nextStep}
|
|
226
|
+
onPrev={prevStep}
|
|
227
|
+
theme={themeConfig}
|
|
228
|
+
/>
|
|
229
|
+
)}
|
|
230
|
+
|
|
231
|
+
{currentStep === 'review' && (
|
|
232
|
+
<ReviewStep
|
|
233
|
+
formData={formData}
|
|
234
|
+
onSubmit={submitKYC}
|
|
235
|
+
onPrev={prevStep}
|
|
236
|
+
isSubmitting={isSubmitting}
|
|
237
|
+
theme={themeConfig}
|
|
238
|
+
/>
|
|
239
|
+
)}
|
|
240
|
+
|
|
241
|
+
{currentStep === 'success' && (
|
|
242
|
+
<SuccessStep
|
|
243
|
+
onClose={onClose}
|
|
244
|
+
theme={themeConfig}
|
|
245
|
+
/>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Step Components
|
|
252
|
+
function IntroStep({ onNext, onClose, theme }: any) {
|
|
253
|
+
return (
|
|
254
|
+
<div className="kyc-step intro-step">
|
|
255
|
+
<div className="step-header">
|
|
256
|
+
<CheckCircle className="step-icon" style={{ color: theme.primaryColor }} />
|
|
257
|
+
<h2>Identity Verification</h2>
|
|
258
|
+
<p>We need to verify your identity to comply with financial regulations. This process takes about 3 minutes.</p>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<div className="verification-info">
|
|
262
|
+
<div className="info-item">
|
|
263
|
+
<span className="info-number">1</span>
|
|
264
|
+
<div>
|
|
265
|
+
<h4>Personal Information</h4>
|
|
266
|
+
<p>Basic details like name and date of birth</p>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
<div className="info-item">
|
|
270
|
+
<span className="info-number">2</span>
|
|
271
|
+
<div>
|
|
272
|
+
<h4>Identification</h4>
|
|
273
|
+
<p>Government-issued ID verification</p>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
<div className="info-item">
|
|
277
|
+
<span className="info-number">3</span>
|
|
278
|
+
<div>
|
|
279
|
+
<h4>Address</h4>
|
|
280
|
+
<p>Current residential address</p>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<div className="step-actions">
|
|
286
|
+
<button
|
|
287
|
+
onClick={onNext}
|
|
288
|
+
className="btn-primary"
|
|
289
|
+
style={{ backgroundColor: theme.primaryColor }}
|
|
290
|
+
>
|
|
291
|
+
Start Verification
|
|
292
|
+
<ArrowRight className="btn-icon" />
|
|
293
|
+
</button>
|
|
294
|
+
{onClose && (
|
|
295
|
+
<button onClick={onClose} className="btn-secondary">
|
|
296
|
+
Not Now
|
|
297
|
+
</button>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function PersonalInfoStep({ formData, updateFormData, onNext, onPrev, theme }: any) {
|
|
305
|
+
const isValid = formData.firstName && formData.lastName && formData.email &&
|
|
306
|
+
formData.dobDay && formData.dobMonth && formData.dobYear;
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
<div className="kyc-step personal-step">
|
|
310
|
+
<div className="step-header">
|
|
311
|
+
<h2>Personal Information</h2>
|
|
312
|
+
<p>Please enter your legal name as it appears on your government-issued ID.</p>
|
|
313
|
+
</div>
|
|
314
|
+
|
|
315
|
+
<form className="step-form">
|
|
316
|
+
<div className="form-row">
|
|
317
|
+
<div className="form-field">
|
|
318
|
+
<label>First Name</label>
|
|
319
|
+
<input
|
|
320
|
+
type="text"
|
|
321
|
+
value={formData.firstName}
|
|
322
|
+
onChange={(e) => updateFormData({ firstName: e.target.value })}
|
|
323
|
+
placeholder="John"
|
|
324
|
+
required
|
|
325
|
+
/>
|
|
326
|
+
</div>
|
|
327
|
+
<div className="form-field">
|
|
328
|
+
<label>Last Name</label>
|
|
329
|
+
<input
|
|
330
|
+
type="text"
|
|
331
|
+
value={formData.lastName}
|
|
332
|
+
onChange={(e) => updateFormData({ lastName: e.target.value })}
|
|
333
|
+
placeholder="Doe"
|
|
334
|
+
required
|
|
335
|
+
/>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<div className="form-field">
|
|
340
|
+
<label>Email Address</label>
|
|
341
|
+
<input
|
|
342
|
+
type="email"
|
|
343
|
+
value={formData.email}
|
|
344
|
+
onChange={(e) => updateFormData({ email: e.target.value })}
|
|
345
|
+
placeholder="john@example.com"
|
|
346
|
+
required
|
|
347
|
+
/>
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
<div className="form-field">
|
|
351
|
+
<label>Date of Birth</label>
|
|
352
|
+
<div className="date-inputs">
|
|
353
|
+
<select
|
|
354
|
+
value={formData.dobMonth}
|
|
355
|
+
onChange={(e) => updateFormData({ dobMonth: e.target.value })}
|
|
356
|
+
required
|
|
357
|
+
>
|
|
358
|
+
<option value="">Month</option>
|
|
359
|
+
{Array.from({ length: 12 }, (_, i) => (
|
|
360
|
+
<option key={i + 1} value={String(i + 1)}>
|
|
361
|
+
{new Date(0, i).toLocaleString('en', { month: 'long' })}
|
|
362
|
+
</option>
|
|
363
|
+
))}
|
|
364
|
+
</select>
|
|
365
|
+
<select
|
|
366
|
+
value={formData.dobDay}
|
|
367
|
+
onChange={(e) => updateFormData({ dobDay: e.target.value })}
|
|
368
|
+
required
|
|
369
|
+
>
|
|
370
|
+
<option value="">Day</option>
|
|
371
|
+
{Array.from({ length: 31 }, (_, i) => (
|
|
372
|
+
<option key={i + 1} value={String(i + 1)}>
|
|
373
|
+
{i + 1}
|
|
374
|
+
</option>
|
|
375
|
+
))}
|
|
376
|
+
</select>
|
|
377
|
+
<select
|
|
378
|
+
value={formData.dobYear}
|
|
379
|
+
onChange={(e) => updateFormData({ dobYear: e.target.value })}
|
|
380
|
+
required
|
|
381
|
+
>
|
|
382
|
+
<option value="">Year</option>
|
|
383
|
+
{Array.from({ length: 100 }, (_, i) => {
|
|
384
|
+
const year = new Date().getFullYear() - 18 - i;
|
|
385
|
+
return (
|
|
386
|
+
<option key={year} value={String(year)}>
|
|
387
|
+
{year}
|
|
388
|
+
</option>
|
|
389
|
+
);
|
|
390
|
+
})}
|
|
391
|
+
</select>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
</form>
|
|
395
|
+
|
|
396
|
+
<div className="step-actions">
|
|
397
|
+
<button onClick={onPrev} className="btn-secondary">
|
|
398
|
+
<ArrowLeft className="btn-icon" />
|
|
399
|
+
Back
|
|
400
|
+
</button>
|
|
401
|
+
<button
|
|
402
|
+
onClick={onNext}
|
|
403
|
+
disabled={!isValid}
|
|
404
|
+
className="btn-primary"
|
|
405
|
+
style={{ backgroundColor: theme.primaryColor }}
|
|
406
|
+
>
|
|
407
|
+
Continue
|
|
408
|
+
<ArrowRight className="btn-icon" />
|
|
409
|
+
</button>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function IdentificationStep({ formData, updateFormData, onNext, onPrev, theme }: any) {
|
|
416
|
+
const isValid = formData.idType && formData.idNumber;
|
|
417
|
+
|
|
418
|
+
return (
|
|
419
|
+
<div className="kyc-step identification-step">
|
|
420
|
+
<div className="step-header">
|
|
421
|
+
<h2>Government ID</h2>
|
|
422
|
+
<p>We need to verify your government-issued identification.</p>
|
|
423
|
+
</div>
|
|
424
|
+
|
|
425
|
+
<form className="step-form">
|
|
426
|
+
<div className="form-field">
|
|
427
|
+
<label>ID Type</label>
|
|
428
|
+
<select
|
|
429
|
+
value={formData.idType}
|
|
430
|
+
onChange={(e) => updateFormData({ idType: e.target.value as 'ssn' | 'tin' | 'passport' })}
|
|
431
|
+
required
|
|
432
|
+
>
|
|
433
|
+
<option value="ssn">Social Security Number (SSN)</option>
|
|
434
|
+
<option value="tin">Taxpayer Identification Number (TIN)</option>
|
|
435
|
+
<option value="passport">Passport Number</option>
|
|
436
|
+
</select>
|
|
437
|
+
</div>
|
|
438
|
+
|
|
439
|
+
<div className="form-field">
|
|
440
|
+
<label>
|
|
441
|
+
{formData.idType === 'ssn' && 'Social Security Number'}
|
|
442
|
+
{formData.idType === 'tin' && 'Taxpayer Identification Number'}
|
|
443
|
+
{formData.idType === 'passport' && 'Passport Number'}
|
|
444
|
+
</label>
|
|
445
|
+
<input
|
|
446
|
+
type="text"
|
|
447
|
+
value={formData.idNumber}
|
|
448
|
+
onChange={(e) => updateFormData({ idNumber: e.target.value })}
|
|
449
|
+
placeholder={
|
|
450
|
+
formData.idType === 'ssn' ? '123-45-6789' :
|
|
451
|
+
formData.idType === 'tin' ? '12-3456789' :
|
|
452
|
+
'A12345678'
|
|
453
|
+
}
|
|
454
|
+
required
|
|
455
|
+
/>
|
|
456
|
+
</div>
|
|
457
|
+
</form>
|
|
458
|
+
|
|
459
|
+
<div className="step-actions">
|
|
460
|
+
<button onClick={onPrev} className="btn-secondary">
|
|
461
|
+
<ArrowLeft className="btn-icon" />
|
|
462
|
+
Back
|
|
463
|
+
</button>
|
|
464
|
+
<button
|
|
465
|
+
onClick={onNext}
|
|
466
|
+
disabled={!isValid}
|
|
467
|
+
className="btn-primary"
|
|
468
|
+
style={{ backgroundColor: theme.primaryColor }}
|
|
469
|
+
>
|
|
470
|
+
Continue
|
|
471
|
+
<ArrowRight className="btn-icon" />
|
|
472
|
+
</button>
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function AddressStep({ formData, updateFormData, onNext, onPrev, theme }: any) {
|
|
479
|
+
const isValid = formData.addressLine1 && formData.city && formData.state &&
|
|
480
|
+
formData.postalCode && formData.country;
|
|
481
|
+
|
|
482
|
+
return (
|
|
483
|
+
<div className="kyc-step address-step">
|
|
484
|
+
<div className="step-header">
|
|
485
|
+
<h2>Address Information</h2>
|
|
486
|
+
<p>Please provide your current residential address.</p>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<form className="step-form">
|
|
490
|
+
<div className="form-field">
|
|
491
|
+
<label>Address Line 1</label>
|
|
492
|
+
<input
|
|
493
|
+
type="text"
|
|
494
|
+
value={formData.addressLine1}
|
|
495
|
+
onChange={(e) => updateFormData({ addressLine1: e.target.value })}
|
|
496
|
+
placeholder="123 Main Street"
|
|
497
|
+
required
|
|
498
|
+
/>
|
|
499
|
+
</div>
|
|
500
|
+
|
|
501
|
+
<div className="form-field">
|
|
502
|
+
<label>Address Line 2 (Optional)</label>
|
|
503
|
+
<input
|
|
504
|
+
type="text"
|
|
505
|
+
value={formData.addressLine2}
|
|
506
|
+
onChange={(e) => updateFormData({ addressLine2: e.target.value })}
|
|
507
|
+
placeholder="Apt 4B"
|
|
508
|
+
/>
|
|
509
|
+
</div>
|
|
510
|
+
|
|
511
|
+
<div className="form-row">
|
|
512
|
+
<div className="form-field">
|
|
513
|
+
<label>City</label>
|
|
514
|
+
<input
|
|
515
|
+
type="text"
|
|
516
|
+
value={formData.city}
|
|
517
|
+
onChange={(e) => updateFormData({ city: e.target.value })}
|
|
518
|
+
placeholder="New York"
|
|
519
|
+
required
|
|
520
|
+
/>
|
|
521
|
+
</div>
|
|
522
|
+
<div className="form-field">
|
|
523
|
+
<label>State</label>
|
|
524
|
+
<input
|
|
525
|
+
type="text"
|
|
526
|
+
value={formData.state}
|
|
527
|
+
onChange={(e) => updateFormData({ state: e.target.value })}
|
|
528
|
+
placeholder="NY"
|
|
529
|
+
required
|
|
530
|
+
/>
|
|
531
|
+
</div>
|
|
532
|
+
</div>
|
|
533
|
+
|
|
534
|
+
<div className="form-row">
|
|
535
|
+
<div className="form-field">
|
|
536
|
+
<label>Postal Code</label>
|
|
537
|
+
<input
|
|
538
|
+
type="text"
|
|
539
|
+
value={formData.postalCode}
|
|
540
|
+
onChange={(e) => updateFormData({ postalCode: e.target.value })}
|
|
541
|
+
placeholder="10001"
|
|
542
|
+
required
|
|
543
|
+
/>
|
|
544
|
+
</div>
|
|
545
|
+
<div className="form-field">
|
|
546
|
+
<label>Country</label>
|
|
547
|
+
<select
|
|
548
|
+
value={formData.country}
|
|
549
|
+
onChange={(e) => updateFormData({ country: e.target.value })}
|
|
550
|
+
required
|
|
551
|
+
>
|
|
552
|
+
<option value="US">United States</option>
|
|
553
|
+
<option value="CA">Canada</option>
|
|
554
|
+
<option value="UK">United Kingdom</option>
|
|
555
|
+
{/* Add more countries as needed */}
|
|
556
|
+
</select>
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
</form>
|
|
560
|
+
|
|
561
|
+
<div className="step-actions">
|
|
562
|
+
<button onClick={onPrev} className="btn-secondary">
|
|
563
|
+
<ArrowLeft className="btn-icon" />
|
|
564
|
+
Back
|
|
565
|
+
</button>
|
|
566
|
+
<button
|
|
567
|
+
onClick={onNext}
|
|
568
|
+
disabled={!isValid}
|
|
569
|
+
className="btn-primary"
|
|
570
|
+
style={{ backgroundColor: theme.primaryColor }}
|
|
571
|
+
>
|
|
572
|
+
Continue
|
|
573
|
+
<ArrowRight className="btn-icon" />
|
|
574
|
+
</button>
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function ReviewStep({ formData, onSubmit, onPrev, isSubmitting, theme }: any) {
|
|
581
|
+
return (
|
|
582
|
+
<div className="kyc-step review-step">
|
|
583
|
+
<div className="step-header">
|
|
584
|
+
<h2>Review Your Information</h2>
|
|
585
|
+
<p>Please review your information before submitting.</p>
|
|
586
|
+
</div>
|
|
587
|
+
|
|
588
|
+
<div className="review-sections">
|
|
589
|
+
<div className="review-section">
|
|
590
|
+
<h3>Personal Information</h3>
|
|
591
|
+
<div className="review-item">
|
|
592
|
+
<span>Name:</span>
|
|
593
|
+
<span>{formData.firstName} {formData.lastName}</span>
|
|
594
|
+
</div>
|
|
595
|
+
<div className="review-item">
|
|
596
|
+
<span>Email:</span>
|
|
597
|
+
<span>{formData.email}</span>
|
|
598
|
+
</div>
|
|
599
|
+
<div className="review-item">
|
|
600
|
+
<span>Date of Birth:</span>
|
|
601
|
+
<span>{formData.dobMonth}/{formData.dobDay}/{formData.dobYear}</span>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
<div className="review-section">
|
|
606
|
+
<h3>Identification</h3>
|
|
607
|
+
<div className="review-item">
|
|
608
|
+
<span>ID Type:</span>
|
|
609
|
+
<span>{formData.idType.toUpperCase()}</span>
|
|
610
|
+
</div>
|
|
611
|
+
<div className="review-item">
|
|
612
|
+
<span>ID Number:</span>
|
|
613
|
+
<span>***-**-{formData.idNumber.slice(-4)}</span>
|
|
614
|
+
</div>
|
|
615
|
+
</div>
|
|
616
|
+
|
|
617
|
+
<div className="review-section">
|
|
618
|
+
<h3>Address</h3>
|
|
619
|
+
<div className="review-item">
|
|
620
|
+
<span>Address:</span>
|
|
621
|
+
<span>
|
|
622
|
+
{formData.addressLine1}
|
|
623
|
+
{formData.addressLine2 && `, ${formData.addressLine2}`}
|
|
624
|
+
</span>
|
|
625
|
+
</div>
|
|
626
|
+
<div className="review-item">
|
|
627
|
+
<span>City, State:</span>
|
|
628
|
+
<span>{formData.city}, {formData.state} {formData.postalCode}</span>
|
|
629
|
+
</div>
|
|
630
|
+
<div className="review-item">
|
|
631
|
+
<span>Country:</span>
|
|
632
|
+
<span>{formData.country}</span>
|
|
633
|
+
</div>
|
|
634
|
+
</div>
|
|
635
|
+
</div>
|
|
636
|
+
|
|
637
|
+
<div className="step-actions">
|
|
638
|
+
<button onClick={onPrev} className="btn-secondary" disabled={isSubmitting}>
|
|
639
|
+
<ArrowLeft className="btn-icon" />
|
|
640
|
+
Back
|
|
641
|
+
</button>
|
|
642
|
+
<button
|
|
643
|
+
onClick={onSubmit}
|
|
644
|
+
disabled={isSubmitting}
|
|
645
|
+
className="btn-primary"
|
|
646
|
+
style={{ backgroundColor: theme.primaryColor }}
|
|
647
|
+
>
|
|
648
|
+
{isSubmitting ? (
|
|
649
|
+
<>
|
|
650
|
+
<Loader2 className="btn-icon animate-spin" />
|
|
651
|
+
Submitting...
|
|
652
|
+
</>
|
|
653
|
+
) : (
|
|
654
|
+
<>
|
|
655
|
+
Submit Verification
|
|
656
|
+
<ArrowRight className="btn-icon" />
|
|
657
|
+
</>
|
|
658
|
+
)}
|
|
659
|
+
</button>
|
|
660
|
+
</div>
|
|
661
|
+
</div>
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function SuccessStep({ onClose, theme }: any) {
|
|
666
|
+
return (
|
|
667
|
+
<div className="kyc-step success-step">
|
|
668
|
+
<div className="step-header">
|
|
669
|
+
<CheckCircle className="step-icon success-icon" style={{ color: theme.primaryColor }} />
|
|
670
|
+
<h2>Verification Submitted!</h2>
|
|
671
|
+
<p>Your identity verification has been submitted successfully. We'll review your information and notify you of the results.</p>
|
|
672
|
+
</div>
|
|
673
|
+
|
|
674
|
+
<div className="success-info">
|
|
675
|
+
<div className="info-box">
|
|
676
|
+
<h4>What happens next?</h4>
|
|
677
|
+
<ul>
|
|
678
|
+
<li>We'll review your information within 24-48 hours</li>
|
|
679
|
+
<li>You'll receive an email with the verification results</li>
|
|
680
|
+
<li>Once approved, you can access all platform features</li>
|
|
681
|
+
</ul>
|
|
682
|
+
</div>
|
|
683
|
+
</div>
|
|
684
|
+
|
|
685
|
+
{onClose && (
|
|
686
|
+
<div className="step-actions">
|
|
687
|
+
<button
|
|
688
|
+
onClick={onClose}
|
|
689
|
+
className="btn-primary"
|
|
690
|
+
style={{ backgroundColor: theme.primaryColor }}
|
|
691
|
+
>
|
|
692
|
+
Continue
|
|
693
|
+
</button>
|
|
694
|
+
</div>
|
|
695
|
+
)}
|
|
696
|
+
</div>
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Export modal version
|
|
701
|
+
export function LiberexKYCModal({ isOpen, onClose, ...props }: LiberexKYCProps & { isOpen: boolean }) {
|
|
702
|
+
if (!isOpen) return null;
|
|
703
|
+
|
|
704
|
+
return (
|
|
705
|
+
<div className="diviswap-kyc-modal-overlay">
|
|
706
|
+
<div className="diviswap-kyc-modal">
|
|
707
|
+
<LiberexKYC {...props} onClose={onClose} />
|
|
708
|
+
</div>
|
|
709
|
+
</div>
|
|
710
|
+
);
|
|
711
|
+
}
|