@casperid/react 1.0.1 → 1.1.0
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/dist/_commonjsHelpers-DKOUU3wS.cjs +2 -0
- package/dist/_commonjsHelpers-DKOUU3wS.cjs.map +1 -0
- package/dist/_commonjsHelpers-DaMA6jEr.js +9 -0
- package/dist/_commonjsHelpers-DaMA6jEr.js.map +1 -0
- package/dist/camera_utils-BQaOSBPu.js +397 -0
- package/dist/camera_utils-BQaOSBPu.js.map +1 -0
- package/dist/camera_utils-wchtqrQn.cjs +2 -0
- package/dist/camera_utils-wchtqrQn.cjs.map +1 -0
- package/dist/face_mesh-DYMPc5Ce.js +4212 -0
- package/dist/face_mesh-DYMPc5Ce.js.map +1 -0
- package/dist/face_mesh-fEvyDoPt.cjs +19 -0
- package/dist/face_mesh-fEvyDoPt.cjs.map +1 -0
- package/dist/index.cjs +41 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1786 -1501
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +11 -11
- package/src/CasperIDModal.tsx +0 -777
- package/src/index.css +0 -217
- package/src/index.ts +0 -41
- package/src/screens/AuthSelection.tsx +0 -221
- package/src/screens/DocumentScan.tsx +0 -348
- package/src/screens/FaceScan.tsx +0 -368
- package/src/screens/IdentityVerified.tsx +0 -51
- package/src/screens/L1Setup.tsx +0 -335
- package/src/screens/Login.tsx +0 -186
- package/src/screens/MintingIdentity.tsx +0 -446
- package/src/screens/PasskeyAuth.tsx +0 -259
- package/src/screens/PasskeyRegister.tsx +0 -281
- package/src/screens/PermissionsRequest.tsx +0 -96
- package/src/screens/PinVerification.tsx +0 -321
- package/src/screens/ReviewData.tsx +0 -315
- package/src/screens/SecurityUpgrade.tsx +0 -83
- package/src/screens/VerifyIdentityChoice.tsx +0 -59
- package/src/shared/Footer.tsx +0 -13
- package/src/shared/GlassContainer.tsx +0 -17
- package/src/shared/Header.tsx +0 -62
- package/src/types.ts +0 -342
- package/src/utils/fetchWithTimeout.ts +0 -52
- package/src/utils/i18n.ts +0 -640
- package/src/utils/webauthn.ts +0 -261
package/src/index.css
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap');
|
|
2
|
-
@import "tailwindcss";
|
|
3
|
-
|
|
4
|
-
@theme {
|
|
5
|
-
--font-sans: "Plus Jakarta Sans", "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/* ============================================
|
|
9
|
-
CSS Variable Bridge — driven by <CasperIDModal theme={...} />
|
|
10
|
-
Variables are set dynamically by the component via scoped styles.
|
|
11
|
-
No defaults here to prevent flash of default colors.
|
|
12
|
-
============================================ */
|
|
13
|
-
:root {
|
|
14
|
-
/* Brand variables are injected by CasperIDModal component */
|
|
15
|
-
font-family: var(--casperid-font, "Plus Jakarta Sans", Inter, sans-serif);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/* Light mode (default) */
|
|
19
|
-
:root,
|
|
20
|
-
.light {
|
|
21
|
-
--bg-app: #f8fafc;
|
|
22
|
-
--text-primary: #0f172a;
|
|
23
|
-
--text-secondary: #64748b;
|
|
24
|
-
--glass-bg: rgba(255, 255, 255, 0.94);
|
|
25
|
-
--glass-border: rgba(0, 0, 0, 0.16);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/* Dark mode */
|
|
29
|
-
.dark {
|
|
30
|
-
--bg-app: #0B0813;
|
|
31
|
-
--text-primary: #ffffff;
|
|
32
|
-
--text-secondary: #94a3b8;
|
|
33
|
-
--glass-bg: #0e0b16;
|
|
34
|
-
--glass-border: rgba(255, 255, 255, 0.18);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@layer base {
|
|
38
|
-
body {
|
|
39
|
-
@apply antialiased;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@layer components {
|
|
44
|
-
.glass-container {
|
|
45
|
-
@apply backdrop-blur-[32px] border transition-all duration-500;
|
|
46
|
-
background: var(--glass-bg);
|
|
47
|
-
border-color: var(--glass-border);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/* Force background color to prevent clipping or transparency issues in light mode */
|
|
51
|
-
:root:not(.dark) .glass-container {
|
|
52
|
-
background-color: var(--glass-bg) !important;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.dark .glass-container {
|
|
56
|
-
background-color: var(--glass-bg) !important;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.light .glass-container {
|
|
60
|
-
box-shadow:
|
|
61
|
-
0 20px 50px -12px rgba(0, 0, 0, 0.1),
|
|
62
|
-
0 0 0 1px rgba(0, 0, 0, 0.05);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.mesh-gradient {
|
|
66
|
-
@apply transition-all duration-700;
|
|
67
|
-
/* Vibrant mesh gradient using the dynamic primary color */
|
|
68
|
-
background:
|
|
69
|
-
radial-gradient(circle at 10% 10%, color-mix(in srgb, var(--casperid-primary), transparent 65%) 0%, transparent 60%),
|
|
70
|
-
radial-gradient(circle at 90% 90%, color-mix(in srgb, var(--casperid-primary), transparent 90%) 0%, transparent 50%);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.dark .mesh-gradient {
|
|
74
|
-
background:
|
|
75
|
-
radial-gradient(circle at 10% 10%, color-mix(in srgb, var(--casperid-primary), transparent 80%) 0%, transparent 50%),
|
|
76
|
-
radial-gradient(circle at 90% 90%, color-mix(in srgb, var(--casperid-primary), transparent 92%) 0%, transparent 40%);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.text-main {
|
|
80
|
-
color: var(--text-primary);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.text-muted {
|
|
84
|
-
color: var(--text-secondary);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.text-brand {
|
|
88
|
-
color: var(--casperid-primary);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* ============================================
|
|
92
|
-
Brand Color Utilities (using CSS variables)
|
|
93
|
-
============================================ */
|
|
94
|
-
.bg-brand {
|
|
95
|
-
background-color: var(--casperid-primary);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.bg-brand-90 {
|
|
99
|
-
background-color: color-mix(in srgb, var(--casperid-primary) 90%, transparent);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.bg-brand-20 {
|
|
103
|
-
background-color: color-mix(in srgb, var(--casperid-primary) 20%, transparent);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.bg-brand-10 {
|
|
107
|
-
background-color: color-mix(in srgb, var(--casperid-primary) 10%, transparent);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.bg-brand-5 {
|
|
111
|
-
background-color: color-mix(in srgb, var(--casperid-primary) 5%, transparent);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.hover\:bg-brand-90:hover {
|
|
115
|
-
background-color: color-mix(in srgb, var(--casperid-primary) 90%, transparent);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.hover\:bg-brand-15:hover {
|
|
119
|
-
background-color: color-mix(in srgb, var(--casperid-primary) 15%, transparent);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.border-brand {
|
|
123
|
-
border-color: var(--casperid-primary);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
.border-brand-30 {
|
|
127
|
-
border-color: color-mix(in srgb, var(--casperid-primary) 30%, transparent);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.border-brand-20 {
|
|
131
|
-
border-color: color-mix(in srgb, var(--casperid-primary) 20%, transparent);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.border-brand-10 {
|
|
135
|
-
border-color: color-mix(in srgb, var(--casperid-primary) 10%, transparent);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.border-brand-5 {
|
|
139
|
-
border-color: color-mix(in srgb, var(--casperid-primary) 5%, transparent);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.border-brand-40 {
|
|
143
|
-
border-color: color-mix(in srgb, var(--casperid-primary) 40%, transparent);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.hover\:border-brand-40:hover {
|
|
147
|
-
border-color: color-mix(in srgb, var(--casperid-primary) 40%, transparent);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.hover\:border-brand-30:hover {
|
|
151
|
-
border-color: color-mix(in srgb, var(--casperid-primary) 30%, transparent);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.shadow-brand {
|
|
155
|
-
box-shadow: 0 10px 25px -5px color-mix(in srgb, var(--casperid-primary) 30%, transparent);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.ring-brand {
|
|
159
|
-
--tw-ring-color: var(--casperid-primary);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.ring-brand-40 {
|
|
163
|
-
--tw-ring-color: color-mix(in srgb, var(--casperid-primary) 40%, transparent);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.selection-brand {
|
|
167
|
-
--tw-selection-bg: color-mix(in srgb, var(--casperid-primary) 30%, transparent);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/* Border radius utility */
|
|
171
|
-
.rounded-brand {
|
|
172
|
-
border-radius: var(--casperid-radius, 12px);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/* Focus ring utilities */
|
|
176
|
-
.focus-within\:ring-brand-40:focus-within {
|
|
177
|
-
--tw-ring-color: color-mix(in srgb, var(--casperid-primary) 40%, transparent);
|
|
178
|
-
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
179
|
-
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
180
|
-
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.focus\:ring-brand-30:focus {
|
|
184
|
-
--tw-ring-color: color-mix(in srgb, var(--casperid-primary) 30%, transparent);
|
|
185
|
-
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
186
|
-
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
187
|
-
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/* Gradient utilities */
|
|
191
|
-
.from-brand {
|
|
192
|
-
--tw-gradient-from: var(--casperid-primary) var(--tw-gradient-from-position);
|
|
193
|
-
--tw-gradient-to: color-mix(in srgb, var(--casperid-primary) 0%, transparent) var(--tw-gradient-to-position);
|
|
194
|
-
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.from-brand-20 {
|
|
198
|
-
--tw-gradient-from: color-mix(in srgb, var(--casperid-primary) 20%, transparent) var(--tw-gradient-from-position);
|
|
199
|
-
--tw-gradient-to: transparent var(--tw-gradient-to-position);
|
|
200
|
-
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.from-brand-5 {
|
|
204
|
-
--tw-gradient-from: color-mix(in srgb, var(--casperid-primary) 5%, transparent) var(--tw-gradient-from-position);
|
|
205
|
-
--tw-gradient-to: transparent var(--tw-gradient-to-position);
|
|
206
|
-
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.to-brand {
|
|
210
|
-
--tw-gradient-to: var(--casperid-primary) var(--tw-gradient-to-position);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.via-brand-10 {
|
|
214
|
-
--tw-gradient-to: transparent var(--tw-gradient-to-position);
|
|
215
|
-
--tw-gradient-stops: var(--tw-gradient-from), color-mix(in srgb, var(--casperid-primary) 10%, transparent) var(--tw-gradient-via-position), var(--tw-gradient-to);
|
|
216
|
-
}
|
|
217
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// Styles (optional - consumers can import separately)
|
|
2
|
-
import './index.css';
|
|
3
|
-
|
|
4
|
-
// Main component
|
|
5
|
-
export { CasperIDModal } from './CasperIDModal';
|
|
6
|
-
export { default } from './CasperIDModal';
|
|
7
|
-
|
|
8
|
-
// Individual screens (for advanced customisation / white-labelling)
|
|
9
|
-
export { default as AuthSelectionScreen } from './screens/AuthSelection';
|
|
10
|
-
export { default as LoginScreen } from './screens/Login';
|
|
11
|
-
export { default as PinVerificationScreen } from './screens/PinVerification';
|
|
12
|
-
export { default as SecurityUpgradeScreen } from './screens/SecurityUpgrade';
|
|
13
|
-
export { default as FaceScanScreen } from './screens/FaceScan';
|
|
14
|
-
export { default as VerifyIdentityChoiceScreen } from './screens/VerifyIdentityChoice';
|
|
15
|
-
export { default as DocumentScanScreen } from './screens/DocumentScan';
|
|
16
|
-
export { default as ReviewDataScreen } from './screens/ReviewData';
|
|
17
|
-
export { default as MintingIdentityScreen } from './screens/MintingIdentity';
|
|
18
|
-
export { default as IdentityVerifiedScreen } from './screens/IdentityVerified';
|
|
19
|
-
export { default as PermissionsRequestScreen } from './screens/PermissionsRequest';
|
|
20
|
-
|
|
21
|
-
// Shared components
|
|
22
|
-
export { default as GlassContainer } from './shared/GlassContainer';
|
|
23
|
-
export { default as Header } from './shared/Header';
|
|
24
|
-
export { default as Footer } from './shared/Footer';
|
|
25
|
-
|
|
26
|
-
// Types
|
|
27
|
-
export type {
|
|
28
|
-
CasperIDModalProps,
|
|
29
|
-
CasperIDTheme,
|
|
30
|
-
Screen,
|
|
31
|
-
SDKMode,
|
|
32
|
-
VerificationTier,
|
|
33
|
-
ScreenProps,
|
|
34
|
-
DocumentType,
|
|
35
|
-
ExtractedDocumentData,
|
|
36
|
-
KYCState,
|
|
37
|
-
OTPState,
|
|
38
|
-
} from './types';
|
|
39
|
-
|
|
40
|
-
// Utilities
|
|
41
|
-
export { fetchWithTimeout, ApiError, handleApiResponse } from './utils/fetchWithTimeout';
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
-
import { motion } from 'framer-motion';
|
|
3
|
-
import { Fingerprint, Puzzle, ChevronRight, Smartphone, ShieldCheck, RefreshCw, Loader2, AlertCircle } from 'lucide-react';
|
|
4
|
-
import { t } from '../utils/i18n';
|
|
5
|
-
|
|
6
|
-
interface ExtensionUserData {
|
|
7
|
-
wallet: string;
|
|
8
|
-
cnsName?: string;
|
|
9
|
-
email?: string;
|
|
10
|
-
tier?: string;
|
|
11
|
-
humanId?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface AuthSelectionProps {
|
|
15
|
-
onNext: (method: 'extension' | 'otp') => void;
|
|
16
|
-
onExtensionSuccess?: (userData: ExtensionUserData) => void;
|
|
17
|
-
onError?: (error: string) => void;
|
|
18
|
-
language?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const AuthSelection: React.FC<AuthSelectionProps> = ({
|
|
22
|
-
onNext,
|
|
23
|
-
onExtensionSuccess,
|
|
24
|
-
onError,
|
|
25
|
-
language = 'EN'
|
|
26
|
-
}) => {
|
|
27
|
-
const [isCheckingExtension, setIsCheckingExtension] = useState(false);
|
|
28
|
-
const [extensionInstalled, setExtensionInstalled] = useState<boolean | null>(null);
|
|
29
|
-
const [error, setError] = useState('');
|
|
30
|
-
|
|
31
|
-
// Check if extension is installed on mount
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
checkExtensionInstalled();
|
|
34
|
-
}, []);
|
|
35
|
-
|
|
36
|
-
// Listen for extension login events
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
const handleExtensionLogin = (event: CustomEvent<ExtensionUserData>) => {
|
|
39
|
-
setIsCheckingExtension(false);
|
|
40
|
-
if (event.detail && event.detail.wallet) {
|
|
41
|
-
onExtensionSuccess?.(event.detail);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
window.addEventListener('casperid-login', handleExtensionLogin as EventListener);
|
|
46
|
-
return () => {
|
|
47
|
-
window.removeEventListener('casperid-login', handleExtensionLogin as EventListener);
|
|
48
|
-
};
|
|
49
|
-
}, [onExtensionSuccess]);
|
|
50
|
-
|
|
51
|
-
const checkExtensionInstalled = useCallback(() => {
|
|
52
|
-
// Check for extension anchor or localStorage data
|
|
53
|
-
const anchor = document.getElementById('casperid-draggable-anchor');
|
|
54
|
-
let storedUser: string | null = null;
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
storedUser = localStorage.getItem('casperid_user');
|
|
58
|
-
} catch {
|
|
59
|
-
// localStorage may not be available in some contexts
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (anchor || storedUser) {
|
|
63
|
-
setExtensionInstalled(true);
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Also check if we can detect extension via a brief timeout
|
|
68
|
-
// Extension injects its content script which adds the anchor
|
|
69
|
-
setTimeout(() => {
|
|
70
|
-
const anchorAfterDelay = document.getElementById('casperid-draggable-anchor');
|
|
71
|
-
setExtensionInstalled(!!anchorAfterDelay);
|
|
72
|
-
}, 500);
|
|
73
|
-
|
|
74
|
-
return false;
|
|
75
|
-
}, []);
|
|
76
|
-
|
|
77
|
-
const isValidUserData = (data: unknown): data is ExtensionUserData => {
|
|
78
|
-
return (
|
|
79
|
-
typeof data === 'object' &&
|
|
80
|
-
data !== null &&
|
|
81
|
-
'wallet' in data &&
|
|
82
|
-
typeof (data as ExtensionUserData).wallet === 'string' &&
|
|
83
|
-
(data as ExtensionUserData).wallet.length > 0
|
|
84
|
-
);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const handleExtensionLogin = async () => {
|
|
88
|
-
setIsCheckingExtension(true);
|
|
89
|
-
setError('');
|
|
90
|
-
|
|
91
|
-
// Check if already logged in via extension
|
|
92
|
-
try {
|
|
93
|
-
const storedUser = localStorage.getItem('casperid_user');
|
|
94
|
-
if (storedUser) {
|
|
95
|
-
const userData = JSON.parse(storedUser);
|
|
96
|
-
if (isValidUserData(userData)) {
|
|
97
|
-
setIsCheckingExtension(false);
|
|
98
|
-
onExtensionSuccess?.(userData);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
} catch {
|
|
103
|
-
// Invalid stored data or localStorage not available, continue with extension trigger
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Check if extension anchor exists
|
|
107
|
-
const anchor = document.getElementById('casperid-draggable-anchor');
|
|
108
|
-
|
|
109
|
-
if (anchor) {
|
|
110
|
-
// Trigger click on the anchor to initiate login
|
|
111
|
-
anchor.click();
|
|
112
|
-
|
|
113
|
-
// Set a timeout to show error if no response
|
|
114
|
-
setTimeout(() => {
|
|
115
|
-
setIsCheckingExtension(false);
|
|
116
|
-
// Check again if login succeeded
|
|
117
|
-
try {
|
|
118
|
-
const newStoredUser = localStorage.getItem('casperid_user');
|
|
119
|
-
if (!newStoredUser) {
|
|
120
|
-
setError(t('extension_connect_msg', language));
|
|
121
|
-
}
|
|
122
|
-
} catch {
|
|
123
|
-
setError(t('extension_connect_msg', language));
|
|
124
|
-
}
|
|
125
|
-
}, 3000);
|
|
126
|
-
} else {
|
|
127
|
-
// Extension not detected
|
|
128
|
-
setIsCheckingExtension(false);
|
|
129
|
-
setError(t('extension_not_detected', language));
|
|
130
|
-
onError?.('Extension not installed');
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const handleOTPLogin = () => {
|
|
135
|
-
setError('');
|
|
136
|
-
onNext('otp');
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<motion.div
|
|
141
|
-
initial={{ opacity: 0, y: 20 }}
|
|
142
|
-
animate={{ opacity: 1, y: 0 }}
|
|
143
|
-
exit={{ opacity: 0, y: -20 }}
|
|
144
|
-
className="flex-1 px-8 flex flex-col justify-center gap-8"
|
|
145
|
-
>
|
|
146
|
-
<div className="text-center space-y-3">
|
|
147
|
-
<div className="relative w-24 h-24 mx-auto mb-6">
|
|
148
|
-
<div className="absolute inset-0 bg-brand-20 blur-2xl rounded-full" />
|
|
149
|
-
<div className="relative w-full h-full glass-container flex items-center justify-center rounded-2xl border border-brand-30">
|
|
150
|
-
<Fingerprint className="w-10 h-10 text-brand" />
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
<h1 className="text-2xl font-extrabold leading-tight tracking-tight text-main">{t('welcome_back', language)}</h1>
|
|
154
|
-
<p className="text-muted text-sm leading-relaxed">
|
|
155
|
-
{t('auth_subtitle', language)}
|
|
156
|
-
</p>
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
{/* Error Message */}
|
|
160
|
-
{error && (
|
|
161
|
-
<div className="flex items-start gap-3 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-2xl">
|
|
162
|
-
<AlertCircle className="w-5 h-5 text-red-500 shrink-0 mt-0.5" />
|
|
163
|
-
<p className="text-red-600 dark:text-red-400 text-sm font-medium">{error}</p>
|
|
164
|
-
</div>
|
|
165
|
-
)}
|
|
166
|
-
|
|
167
|
-
<div className="space-y-4">
|
|
168
|
-
<button
|
|
169
|
-
onClick={handleExtensionLogin}
|
|
170
|
-
disabled={isCheckingExtension}
|
|
171
|
-
className="w-full group relative overflow-hidden flex items-center gap-4 bg-brand hover:bg-brand-90 text-white py-5 px-6 rounded-2xl font-bold transition-all transform active:scale-[0.98] disabled:opacity-70 disabled:cursor-not-allowed shadow-brand"
|
|
172
|
-
>
|
|
173
|
-
<div className="bg-white/20 p-2 rounded-lg">
|
|
174
|
-
{isCheckingExtension ? (
|
|
175
|
-
<Loader2 className="w-5 h-5 animate-spin" />
|
|
176
|
-
) : (
|
|
177
|
-
<Puzzle className="w-5 h-5" />
|
|
178
|
-
)}
|
|
179
|
-
</div>
|
|
180
|
-
<div className="flex flex-col items-start">
|
|
181
|
-
<span className="text-base">
|
|
182
|
-
{isCheckingExtension ? t('connecting', language) : t('sign_in_with_extension', language)}
|
|
183
|
-
</span>
|
|
184
|
-
<span className="text-[10px] uppercase tracking-widest text-white/70">
|
|
185
|
-
{extensionInstalled === true ? t('extension_detected', language) : t('secure_wallet', language)}
|
|
186
|
-
</span>
|
|
187
|
-
</div>
|
|
188
|
-
<ChevronRight className="ml-auto w-4 h-4 opacity-50 group-hover:opacity-100 transition-opacity" />
|
|
189
|
-
</button>
|
|
190
|
-
|
|
191
|
-
<button
|
|
192
|
-
onClick={handleOTPLogin}
|
|
193
|
-
disabled={isCheckingExtension}
|
|
194
|
-
className="w-full group relative overflow-hidden flex items-center gap-4 dark:bg-white/5 bg-slate-100/50 hover:dark:bg-white/10 hover:bg-slate-200/80 border dark:border-white/10 border-slate-200 text-main py-5 px-6 rounded-2xl font-bold transition-all transform active:scale-[0.98] disabled:opacity-50"
|
|
195
|
-
>
|
|
196
|
-
<div className="bg-brand-10 p-2 rounded-lg">
|
|
197
|
-
<Smartphone className="w-5 h-5 text-brand" />
|
|
198
|
-
</div>
|
|
199
|
-
<div className="flex flex-col items-start">
|
|
200
|
-
<span className="text-base">{t('sign_in_with_otp', language)}</span>
|
|
201
|
-
<span className="text-[10px] uppercase tracking-widest text-brand font-bold">{t('two_factor_auth', language)}</span>
|
|
202
|
-
</div>
|
|
203
|
-
<ChevronRight className="ml-auto w-4 h-4 opacity-50 group-hover:opacity-100 transition-opacity" />
|
|
204
|
-
</button>
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<div className="grid grid-cols-2 gap-3">
|
|
208
|
-
<div className="glass-container p-4 rounded-2xl flex flex-col items-center gap-2 text-center border-slate-200/50">
|
|
209
|
-
<ShieldCheck className="w-5 h-5 text-brand" />
|
|
210
|
-
<span className="text-[10px] font-bold text-muted">{t('fully_audited', language)}</span>
|
|
211
|
-
</div>
|
|
212
|
-
<div className="glass-container p-4 rounded-2xl flex flex-col items-center gap-2 text-center border-slate-200/50">
|
|
213
|
-
<RefreshCw className="w-5 h-5 text-brand" />
|
|
214
|
-
<span className="text-[10px] font-bold text-muted">{t('non_custodial', language)}</span>
|
|
215
|
-
</div>
|
|
216
|
-
</div>
|
|
217
|
-
</motion.div>
|
|
218
|
-
);
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
export default AuthSelection;
|